This is an automated email from the ASF dual-hosted git repository.
amc 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 fe24de0 Prevents response body content with a 204
fe24de0 is described below
commit fe24de0f50a6692d6eb9f0eb9fbcf68d414fe4c7
Author: Derek Dagit <[email protected]>
AuthorDate: Mon Jun 12 15:23:15 2017 +0000
Prevents response body content with a 204
---
proxy/http/HttpBodyFactory.cc | 10 ++
proxy/http/HttpSM.cc | 4 +-
proxy/http/HttpTransact.cc | 17 +--
proxy/http/HttpTransact.h | 2 +-
tests/autest.sh | 2 +-
.../gold_tests/autest-site/trafficserver.test.ext | 5 +
.../autest-site/trafficserver_plugins.test.ext | 44 ++++++
.../data/www.customplugin204.test_get.txt | 2 +
.../data/www.customtemplate204.test_get.txt | 2 +
.../body_factory/data/www.default204.test_get.txt | 2 +
.../body_factory/gold/http-204-custom-plugin.gold | 19 +++
.../body_factory/gold/http-204-custom.gold | 21 +++
tests/gold_tests/body_factory/gold/http-204.gold | 4 +
.../body_factory/http204_response.test.py | 93 +++++++++++++
.../body_factory/http204_response_plugin.test.py | 52 +++++++
tests/tools/plugins/custom204plugin.cc | 153 +++++++++++++++++++++
tests/tools/tcp_client.py | 59 ++++++++
17 files changed, 479 insertions(+), 12 deletions(-)
diff --git a/proxy/http/HttpBodyFactory.cc b/proxy/http/HttpBodyFactory.cc
index dade119..ea09481 100644
--- a/proxy/http/HttpBodyFactory.cc
+++ b/proxy/http/HttpBodyFactory.cc
@@ -138,6 +138,11 @@ HttpBodyFactory::fabricate_with_old_api(const char *type,
HttpTransact::State *c
// if failed, try to fabricate the default custom response //
/////////////////////////////////////////////////////////////
if (buffer == nullptr) {
+ if (is_response_body_precluded(context->http_return_code,
context->method)) {
+ *resulting_buffer_length = 0;
+ unlock();
+ return nullptr;
+ }
buffer = fabricate(&acpt_language_list, &acpt_charset_list, "default",
context, resulting_buffer_length, &lang_ptr,
&charset_ptr, &set);
}
@@ -406,6 +411,8 @@ HttpBodyFactory::fabricate(StrList *acpt_language_list,
StrList *acpt_charset_li
set = determine_set_by_language(acpt_language_list, acpt_charset_list);
} else if (enable_customizations == 3) {
set = determine_set_by_host(context);
+ } else if (is_response_body_precluded(context->http_return_code,
context->method)) {
+ return nullptr;
} else {
set = "default";
}
@@ -424,6 +431,9 @@ HttpBodyFactory::fabricate(StrList *acpt_language_list,
StrList *acpt_charset_li
// Check for base customizations if specializations didn't work.
if (t == nullptr) {
+ if (is_response_body_precluded(context->http_return_code,
context->method)) {
+ return nullptr;
+ }
t = find_template(set, type, &body_set); // this executes if the
template_base is wrong and doesn't exist
}
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index e70be84..eb11a48 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -6119,10 +6119,10 @@ HttpSM::setup_100_continue_transfer()
void
HttpSM::setup_error_transfer()
{
- if (t_state.internal_msg_buffer) {
+ if (t_state.internal_msg_buffer || t_state.http_return_code ==
HTTP_STATUS_NO_CONTENT) {
// Since we need to send the error message, call the API
// function
- ink_assert(t_state.internal_msg_buffer_size > 0);
+ ink_assert(t_state.internal_msg_buffer_size > 0 ||
t_state.http_return_code == HTTP_STATUS_NO_CONTENT);
t_state.api_next_action = HttpTransact::SM_ACTION_API_SEND_RESPONSE_HDR;
do_api_callout();
} else {
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 03b698a..49597a4 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -7822,11 +7822,6 @@ HttpTransact::build_response(State *s, HTTPHdr
*base_response, HTTPHdr *outgoing
HttpTransactHeaders::add_server_header_to_response(s->txn_conf,
outgoing_response);
- // auth-response update
- // if (!s->state_machine->authAdapter.disabled()) {
- // s->state_machine->authAdapter.UpdateResponseHeaders(outgoing_response);
- // }
-
if (!s->cop_test_page && is_debug_tag_set("http_hdrs")) {
if (base_response) {
DUMP_HEADER("http_hdrs", base_response, s->state_machine_id, "Base
Header for Building Response");
@@ -8011,9 +8006,15 @@ HttpTransact::build_error_response(State *s, HTTPStatus
status_code, const char
s->internal_msg_buffer_size = len;
s->internal_msg_buffer_fast_allocator_size = -1;
- s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE,
MIME_LEN_CONTENT_TYPE, body_type, strlen(body_type));
- s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_LANGUAGE,
MIME_LEN_CONTENT_LANGUAGE, body_language,
- strlen(body_language));
+ if (!is_response_body_precluded(status_code, s->method) || len > 0) {
+ // Plugins may create response bodies despite an HTTP spec violation.
+ s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_TYPE,
MIME_LEN_CONTENT_TYPE, body_type, strlen(body_type));
+ s->hdr_info.client_response.value_set(MIME_FIELD_CONTENT_LANGUAGE,
MIME_LEN_CONTENT_LANGUAGE, body_language,
+ strlen(body_language));
+ } else {
+ s->hdr_info.client_response.field_delete(MIME_FIELD_CONTENT_TYPE,
MIME_LEN_CONTENT_TYPE);
+ s->hdr_info.client_response.field_delete(MIME_FIELD_CONTENT_LANGUAGE,
MIME_LEN_CONTENT_LANGUAGE);
+ }
////////////////////////////////////////
// log a description in the error log //
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 338bcd8..ed1956a 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -1164,7 +1164,7 @@ is_response_body_precluded(HTTPStatus status_code, int
method)
if (((status_code != HTTP_STATUS_OK) &&
((status_code == HTTP_STATUS_NOT_MODIFIED) || ((status_code <
HTTP_STATUS_OK) && (status_code >= HTTP_STATUS_CONTINUE)) ||
- (status_code == 204))) ||
+ (status_code == HTTP_STATUS_NO_CONTENT))) ||
(method == HTTP_WKSIDX_HEAD)) {
return true;
} else {
diff --git a/tests/autest.sh b/tests/autest.sh
index 45f4d51..33c4d21 100755
--- a/tests/autest.sh
+++ b/tests/autest.sh
@@ -29,7 +29,7 @@ if [ ! -f ./env-test/bin/autest ]; then\
# this is for rhel or centos systems
test -r /opt/rh/rh-python35/enable && . /opt/rh/rh-python35/enable
. env-test/bin/activate
-./env-test/bin/autest -D gold_tests $*
+./env-test/bin/autest -D gold_tests "$@"
ret=$?
popd > /dev/null
exit $ret
diff --git a/tests/gold_tests/autest-site/trafficserver.test.ext
b/tests/gold_tests/autest-site/trafficserver.test.ext
index f4442e0..ba64614 100644
--- a/tests/gold_tests/autest-site/trafficserver.test.ext
+++ b/tests/gold_tests/autest-site/trafficserver.test.ext
@@ -209,6 +209,11 @@ def MakeATSProcess(obj, name, command='traffic_server',
select_ports=True):
tmpname = os.path.join(config_dir, fname)
p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
+ # This is for regex_remap plugin.
+ fname = "maps.reg"
+ tmpname = os.path.join(config_dir, fname)
+ p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
+
fname = "socks.config"
tmpname = os.path.join(config_dir, fname)
p.Disk.File(tmpname, id=make_id(fname), typename="ats:config")
diff --git a/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
b/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
new file mode 100644
index 0000000..fb53bd7
--- /dev/null
+++ b/tests/gold_tests/autest-site/trafficserver_plugins.test.ext
@@ -0,0 +1,44 @@
+'''
+Builds, installs, and enables an ATS plugin in the sandbox environment
+'''
+# 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.
+
+import os
+
+def prepare_plugin(self, path, tsproc):
+ """Builds, installs, and enables an ATS plugin in the sandbox environment
+
+ The source file at the given path is copied to the sandbox directory of the
+ given traffic server process and compiled into a binary with the file
+ extensioned replaced with '.so'. An entry for this plugin is added to
+ the 'plugin.config' file."""
+
+ # Copy the source to the sandbox directory.
+ plugin_dir = tsproc.Env['PROXY_CONFIG_PLUGIN_PLUGIN_DIR']
+ tsproc.Setup.Copy(path, plugin_dir)
+
+ # Compile the plugin.
+ in_basename = os.path.basename(path)
+ in_path = os.path.join(plugin_dir, in_basename)
+ out_basename = os.path.splitext(in_basename)[0] + '.so'
+ out_path = os.path.join(plugin_dir, out_basename)
+ tsproc.Setup.RunCommand("tsxs -c {0} -o {1}".format(in_path, out_path))
+
+ # Add an entry to plugin.config.
+ tsproc.Disk.plugin_config.AddLine(out_basename)
+
+ExtendTest(prepare_plugin, name="prepare_plugin")
diff --git
a/tests/gold_tests/body_factory/data/www.customplugin204.test_get.txt
b/tests/gold_tests/body_factory/data/www.customplugin204.test_get.txt
new file mode 100644
index 0000000..be603f9
--- /dev/null
+++ b/tests/gold_tests/body_factory/data/www.customplugin204.test_get.txt
@@ -0,0 +1,2 @@
+GET HTTP://www.customplugin204.test/ HTTP/1.1
+
diff --git
a/tests/gold_tests/body_factory/data/www.customtemplate204.test_get.txt
b/tests/gold_tests/body_factory/data/www.customtemplate204.test_get.txt
new file mode 100644
index 0000000..395d798
--- /dev/null
+++ b/tests/gold_tests/body_factory/data/www.customtemplate204.test_get.txt
@@ -0,0 +1,2 @@
+GET HTTP://www.customtemplate204.test/ HTTP/1.1
+
diff --git a/tests/gold_tests/body_factory/data/www.default204.test_get.txt
b/tests/gold_tests/body_factory/data/www.default204.test_get.txt
new file mode 100644
index 0000000..e77408a
--- /dev/null
+++ b/tests/gold_tests/body_factory/data/www.default204.test_get.txt
@@ -0,0 +1,2 @@
+GET HTTP://www.default204.test/ HTTP/1.1
+
diff --git a/tests/gold_tests/body_factory/gold/http-204-custom-plugin.gold
b/tests/gold_tests/body_factory/gold/http-204-custom-plugin.gold
new file mode 100644
index 0000000..cab77b6
--- /dev/null
+++ b/tests/gold_tests/body_factory/gold/http-204-custom-plugin.gold
@@ -0,0 +1,19 @@
+HTTP/1.1 204 No Content
+Connection: keep-alive
+Cache-Control: no-store
+Content-Length: 282
+Content-Type: text/html
+
+<HTML>
+<HEAD>
+<TITLE>Spec-breaking 204!</TITLE>
+</HEAD>
+
+<BODY>
+<H1>This is body content for a 204.</H1>
+<HR>
+
+Description: According to rfc7231 I should not have been sent to you!<BR/>
+This response was sent via the custom204plugin via a call to
TSHttpTxnErrorBodySet.
+<HR>
+</BODY>
diff --git a/tests/gold_tests/body_factory/gold/http-204-custom.gold
b/tests/gold_tests/body_factory/gold/http-204-custom.gold
new file mode 100644
index 0000000..fb84d9b
--- /dev/null
+++ b/tests/gold_tests/body_factory/gold/http-204-custom.gold
@@ -0,0 +1,21 @@
+HTTP/1.1 204 No Content
+Connection: keep-alive
+Cache-Control: no-store
+Content-Type: text/html
+Content-Language: en
+Content-Length: 271
+
+<HTML>
+<HEAD>
+<TITLE>Spec-breaking 204!</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="white" FGCOLOR="black">
+<H1>This is body content for a 204.</H1>
+<HR>
+
+<FONT FACE="Helvetica,Arial"><B>
+Description: According to rfc7231 I should not have been sent to you!
+</B></FONT>
+<HR>
+</BODY>
diff --git a/tests/gold_tests/body_factory/gold/http-204.gold
b/tests/gold_tests/body_factory/gold/http-204.gold
new file mode 100644
index 0000000..2931202
--- /dev/null
+++ b/tests/gold_tests/body_factory/gold/http-204.gold
@@ -0,0 +1,4 @@
+HTTP/1.1 204 No Content
+Connection: keep-alive
+Cache-Control: no-store
+
diff --git a/tests/gold_tests/body_factory/http204_response.test.py
b/tests/gold_tests/body_factory/http204_response.test.py
new file mode 100644
index 0000000..1fb748f
--- /dev/null
+++ b/tests/gold_tests/body_factory/http204_response.test.py
@@ -0,0 +1,93 @@
+'''
+Tests that 204 responses conform to rfc2616, unless custom templates override.
+'''
+# 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.
+
+import os
+
+Test.Summary = '''
+Tests that 204 responses conform to rfc2616, unless custom templates override.
+'''
+
+Test.SkipUnless(Condition.HasProgram("grep","grep needs to be installed on
system for this test to work"))
+
+ts=Test.MakeATSProcess("ts")
+server=Test.MakeOriginServer("server")
+
+DEFAULT_204_HOST='www.default204.test'
+CUSTOM_TEMPLATE_204_HOST='www.customtemplate204.test'
+
+ts.Disk.records_config.update({
+ # enable domain specific body factory
+ 'proxy.config.body_factory.enable_customizations': 3,
+ })
+
+# Create a template body for a 204.
+body_factory_dir=ts.Variables.body_factory_template_dir
+ts.Disk.File(os.path.join(body_factory_dir, 'default',
CUSTOM_TEMPLATE_204_HOST+'_default')).\
+ WriteOn(
+"""<HTML>
+<HEAD>
+<TITLE>Spec-breaking 204!</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="white" FGCOLOR="black">
+<H1>This is body content for a 204.</H1>
+<HR>
+
+<FONT FACE="Helvetica,Arial"><B>
+Description: According to rfc7231 I should not have been sent to you!
+</B></FONT>
+<HR>
+</BODY>
+""")
+
+ts.Disk.remap_config.AddLine(
+ 'map http://{0} http://127.0.0.1:{1} @plugin=regex_remap.so
@pparam=maps.reg @pparam=no-query-string @pparam=host'
+ .format(DEFAULT_204_HOST, server.Variables.Port)
+ )
+ts.Disk.remap_config.AddLine(
+ 'map http://{0} http://127.0.0.1:{1} @plugin=regex_remap.so
@pparam=maps.reg @pparam=no-query-string @pparam=host @plugin=conf_remap.so
@pparam=proxy.config.body_factory.template_base={0}'
+ .format(CUSTOM_TEMPLATE_204_HOST, server.Variables.Port)
+ )
+ts.Disk.maps_reg.AddLine(
+ '//.*/ http://127.0.0.1:{0} @status=204'
+ .format(server.Variables.Port)
+ )
+
+Test.Setup.Copy(os.path.join(os.pardir,os.pardir,'tools','tcp_client.py'))
+Test.Setup.Copy('data')
+
+defaultTr=Test.AddTestRun("Test domain {0}".format(DEFAULT_204_HOST))
+defaultTr.Processes.Default.StartBefore(Test.Processes.ts)
+defaultTr.StillRunningAfter = ts
+
+defaultTr.Processes.Default.Command="python tcp_client.py 127.0.0.1 {0} {1} |
grep -v '^Date: '| grep -v '^Server: ATS/'".\
+ format(ts.Variables.port, 'data/{0}_get.txt'.format(DEFAULT_204_HOST))
+defaultTr.Processes.Default.TimeOut=5 # seconds
+defaultTr.Processes.Default.ReturnCode=0
+defaultTr.Processes.Default.Streams.stdout="gold/http-204.gold"
+
+
+customTemplateTr=Test.AddTestRun("Test domain
{0}".format(CUSTOM_TEMPLATE_204_HOST))
+customTemplateTr.StillRunningBefore = ts
+customTemplateTr.StillRunningAfter = ts
+customTemplateTr.Processes.Default.Command="python tcp_client.py 127.0.0.1 {0}
{1} | grep -v '^Date: '| grep -v '^Server: ATS/'".\
+ format(ts.Variables.port,
'data/{0}_get.txt'.format(CUSTOM_TEMPLATE_204_HOST))
+customTemplateTr.Processes.Default.TimeOut=5 # seconds
+customTemplateTr.Processes.Default.ReturnCode=0
+customTemplateTr.Processes.Default.Streams.stdout="gold/http-204-custom.gold"
diff --git a/tests/gold_tests/body_factory/http204_response_plugin.test.py
b/tests/gold_tests/body_factory/http204_response_plugin.test.py
new file mode 100644
index 0000000..ee3d498
--- /dev/null
+++ b/tests/gold_tests/body_factory/http204_response_plugin.test.py
@@ -0,0 +1,52 @@
+'''
+Tests that plugins may break HTTP by sending 204 respose bodies
+'''
+# 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.
+
+import os
+
+Test.Summary = '''
+Tests that plugins may break HTTP by sending 204 respose bodies
+'''
+
+Test.SkipUnless(Condition.HasProgram("grep","grep needs to be installed on
system for this test to work"))
+
+ts=Test.MakeATSProcess("ts")
+server=Test.MakeOriginServer("server")
+
+CUSTOM_PLUGIN_204_HOST='www.customplugin204.test'
+
+ts.Disk.remap_config.AddLine(
+ 'map http://{0} http://127.0.0.1:{1} @plugin=regex_remap.so
@pparam=maps.reg @pparam=no-query-string @pparam=host'
+ .format(CUSTOM_PLUGIN_204_HOST, server.Variables.Port)
+ )
+ts.Disk.maps_reg.AddLine('//.*/ http://donotcare.test @status=204')
+
+Test.prepare_plugin(os.path.join(Test.Variables.AtsTestToolsDir, 'plugins',
'custom204plugin.cc'), ts)
+
+Test.Setup.Copy(os.path.join(os.pardir,os.pardir,'tools','tcp_client.py'))
+Test.Setup.Copy('data')
+
+tr=Test.AddTestRun("Test domain {0}".format(CUSTOM_PLUGIN_204_HOST))
+tr.Processes.Default.StartBefore(Test.Processes.ts)
+tr.StillRunningAfter = ts
+
+tr.Processes.Default.Command="python tcp_client.py 127.0.0.1 {0} {1} | grep -v
'^Date: '| grep -v '^Server: ATS/'".\
+ format(ts.Variables.port,
'data/{0}_get.txt'.format(CUSTOM_PLUGIN_204_HOST))
+tr.Processes.Default.TimeOut=5 # seconds
+tr.Processes.Default.ReturnCode=0
+tr.Processes.Default.Streams.stdout="gold/http-204-custom-plugin.gold"
diff --git a/tests/tools/plugins/custom204plugin.cc
b/tests/tools/plugins/custom204plugin.cc
new file mode 100644
index 0000000..98fde58
--- /dev/null
+++ b/tests/tools/plugins/custom204plugin.cc
@@ -0,0 +1,153 @@
+/** @file
+
+ A plugin that sets custom 204 response bodies.
+
+ @section license License
+
+ 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.
+ */
+
+#include "ts/ts.h"
+#include "string.h"
+
+#define PLUGIN_NAME "custom204plugintest"
+
+static int
+local_handler(TSCont contp, TSEvent event, void *edata) {
+
+ const char *msg =
+"<HTML>\n"
+"<HEAD>\n"
+"<TITLE>Spec-breaking 204!</TITLE>\n"
+"</HEAD>\n"
+"\n"
+"<BODY>\n"
+"<H1>This is body content for a 204.</H1>\n"
+"<HR>\n"
+"\n"
+"Description: According to rfc7231 I should not have been sent to you!<BR/>\n"
+"This response was sent via the custom204plugin via a call to
TSHttpTxnErrorBodySet.\n"
+"<HR>\n"
+"</BODY>";
+ TSHttpTxn txnp = (TSHttpTxn) edata;
+ TSMBuffer bufp = nullptr;
+ TSMLoc hdr_loc = nullptr;
+ TSMLoc url_loc = nullptr;;
+ const char *host = nullptr;
+ int host_length;
+ const char *test_host = "www.customplugin204.test";
+
+ switch (event) {
+ case TS_EVENT_HTTP_PRE_REMAP:
+ TSDebug(PLUGIN_NAME, "event TS_EVENT_HTTP_PRE_REMAP received");
+ TSDebug(PLUGIN_NAME, "running plugin logic.");
+ if (TSHttpTxnClientReqGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+ TSDebug(PLUGIN_NAME, "Couldn't retrieve client request header");
+ TSError("[%s] Couldn't retrieve client request header", PLUGIN_NAME);
+ goto done;
+ }
+ TSDebug(PLUGIN_NAME, "got client request");
+
+ if (TSHttpHdrUrlGet(bufp, hdr_loc, &url_loc) != TS_SUCCESS) {
+ TSError("[%s] Couldn't retrieve request url", PLUGIN_NAME);
+ TSDebug(PLUGIN_NAME, "Couldn't retrieve request url");
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ goto done;
+ }
+ TSDebug(PLUGIN_NAME, "got client request url");
+
+ host = TSUrlHostGet(bufp, url_loc, &host_length);
+ if (!host) {
+ TSError("[%s] Couldn't retrieve request hostname", PLUGIN_NAME);
+ TSDebug(PLUGIN_NAME, "Couldn't retrieve request hostname");
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ goto done;
+ }
+ TSDebug(PLUGIN_NAME, "request's host was retrieved");
+
+ if (strncmp(host, test_host, strlen(test_host)) == 0) {
+ TSDebug(PLUGIN_NAME, "host matches, hook
TS_HTTP_SEND_RESPONSE_HDR_HOOK");
+ TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, contp);
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 0;
+ }
+ TSDebug(PLUGIN_NAME, "Host != expected host '%s'", test_host);
+ break;
+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+ TSDebug(PLUGIN_NAME, "Returning 204 with custom response body.");
+ TSHttpTxnSetHttpRetStatus(txnp, TS_HTTP_STATUS_NO_CONTENT);
+ TSHttpTxnErrorBodySet(txnp, TSstrdup(msg), strlen(msg),
+ TSstrdup("text/html"));
+ break;
+
+ case TS_EVENT_HTTP_TXN_CLOSE:
+ TSDebug(PLUGIN_NAME, "event TS_EVENT_HTTP_TXN_CLOSE received");
+ TSContDestroy(contp);
+ break;
+
+ default:
+ TSAssert(!"Unexpected event");
+ break;
+
+ }
+
+done:
+ TSHandleMLocRelease(bufp, hdr_loc, url_loc);
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 1;
+}
+
+static int
+global_handler(TSCont contp, TSEvent event, void *edata) {
+ TSHttpTxn txnp = (TSHttpTxn) edata;
+ TSCont txn_contp = nullptr;
+
+ switch(event) {
+ case TS_EVENT_HTTP_TXN_START:
+ txn_contp = TSContCreate(local_handler, TSMutexCreate());
+ TSHttpTxnHookAdd(txnp, TS_HTTP_PRE_REMAP_HOOK, txn_contp);
+ TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
+ TSDebug(PLUGIN_NAME, "hooked TS_HTTP_OS_DNS_HOOK and
TS_EVENT_HTTP_TXN_CLOSE_HOOK");
+ break;
+ default:
+ TSAssert(!"Unexpected event");
+ break;
+ }
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 1;
+}
+
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+
+ info.plugin_name = PLUGIN_NAME;
+ info.vendor_name = "Apache Software Foundation";
+ info.support_email = "[email protected]";
+
+ if (TSPluginRegister(&info) != TS_SUCCESS) {
+ TSError("[%s] Plugin registration failed", PLUGIN_NAME);
+ }
+
+ TSCont contp = TSContCreate(global_handler, TSMutexCreate());
+ TSHttpHookAdd(TS_HTTP_TXN_START_HOOK, contp);
+}
diff --git a/tests/tools/tcp_client.py b/tests/tools/tcp_client.py
new file mode 100644
index 0000000..2fb0c00
--- /dev/null
+++ b/tests/tools/tcp_client.py
@@ -0,0 +1,59 @@
+'''
+A simple command line interface to send/receive bytes over TCP.
+'''
+# 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.
+
+import argparse
+import socket
+import sys
+
+def tcp_client(host, port, data):
+ pass
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((host, port))
+ s.sendall(data.encode())
+ s.shutdown(socket.SHUT_WR)
+ while True:
+ output = s.recv(4096) # suggested bufsize from docs.python.org
+ if len(output) <= 0:
+ break
+ else:
+ sys.stdout.write(output.decode())
+ s.close()
+
+DESCRIPTION=\
+"""A simple command line interface to send/receive bytes over TCP.
+
+The full contents of the given file are sent via a TCP connection to the given
+host and port. Then data is read from the connection and printed to standard
+output. Streaming is not supported."""
+
+def main(argv):
+ parser = argparse.ArgumentParser(description=DESCRIPTION)
+ parser.add_argument('host', help='the target host')
+ parser.add_argument('port', type=int, help='the target port')
+ parser.add_argument('file', help='the file with content to be sent')
+ args = parser.parse_args()
+
+ data = ''
+ with open(args.file, 'r') as f:
+ data = f.read()
+
+ tcp_client(args.host, args.port, data)
+
+if __name__ == "__main__":
+ main(sys.argv)
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].