This is an automated email from the ASF dual-hosted git repository. zwoop pushed a commit to branch 7.1.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/7.1.x by this push: new 1931ba0 Prevents response body content with a 204 1931ba0 is described below commit 1931ba095d0efb1e0b4a859fc754ff11e9f9b9c6 Author: Derek Dagit <der...@yahoo-inc.com> AuthorDate: Mon Jun 12 15:23:15 2017 +0000 Prevents response body content with a 204 (cherry picked from commit fe24de0f50a6692d6eb9f0eb9fbcf68d414fe4c7) --- 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 edec63f..3d3ddcf 100644 --- a/proxy/http/HttpBodyFactory.cc +++ b/proxy/http/HttpBodyFactory.cc @@ -146,6 +146,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); } @@ -429,6 +434,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"; } @@ -443,6 +450,9 @@ HttpBodyFactory::fabricate(StrList *acpt_language_list, StrList *acpt_charset_li // see if we have a custom error page template t = find_template(set, template_base, &body_set); 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 } if (t == nullptr) { diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc index c8da407..fc21966 100644 --- a/proxy/http/HttpSM.cc +++ b/proxy/http/HttpSM.cc @@ -6271,10 +6271,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 631decf..68716b2 100644 --- a/proxy/http/HttpTransact.cc +++ b/proxy/http/HttpTransact.cc @@ -8044,11 +8044,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"); @@ -8236,9 +8231,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 33fa650..4791919 100644 --- a/proxy/http/HttpTransact.h +++ b/proxy/http/HttpTransact.h @@ -1363,7 +1363,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 6d4005e..e9245f6 100644 --- a/tests/gold_tests/autest-site/trafficserver.test.ext +++ b/tests/gold_tests/autest-site/trafficserver.test.ext @@ -213,6 +213,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 = "d...@trafficserver.apache.org"; + + 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 ['"commits@trafficserver.apache.org" <commits@trafficserver.apache.org>'].