diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst index 12f9b4acc25..c3419e125f3 100644 --- a/doc/admin-guide/plugins/header_rewrite.en.rst +++ b/doc/admin-guide/plugins/header_rewrite.en.rst @@ -528,6 +528,24 @@ Refer to `Requests vs. Responses`_ for more information on determining the context in which the transaction's URL is evaluated. The ```` may be specified according to the options documented in `URL Parts`_. +SSN-TXN-COUNT +~~~~~~~~~~~~~ +:: + + cond %{SSN-TXN-COUNT} + +Returns the number of transactions between the Traffic Server proxy and the origin server from a single session. +Any value greater than zero indicates connection reuse. + +TCP-INFO +~~~~~~~~ +:: + + cond %{} + add-header @PropertyName "%{TCP-INFO}" + +This operation records TCP Info struct field values as an Internal remap as well as global header at the event hook specified by the condition. Supported hook conditions include TXN_START_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Global plugin and REMAP_PSEUDO_HOOK, SEND_RESPONSE_HEADER_HOOK and TXN_CLOSE_HOOK in the Remap plugin. Conditions supported as request headers include TXN_START_HOOK and REMAP_PSEUDO_HOOK. The other conditions are supported as response headers. TCP Info fields currently recorded include rtt, rto, snd_cwnd and all_retrans. This operation is not supported on transactions originated within Traffic Server (for e.g using the |TS| :c:func:`TSHttpTxnIsInternal`) + Condition Operands ------------------ @@ -995,6 +1013,15 @@ cache updates may have been performed. This hook context provides a means to modify aspects of the response sent to a client, while still caching the original versions of those attributes delivered by the origin server. +TXN_START_HOOK +~~~~~~~~~~~~~~ +Rulesets are evaluated when |TS| receives a request and accepts it. This hook context indicates that a HTTP transaction is initiated and therefore, can only be enabled as a global plugin. + +TXN_CLOSE_HOOK +~~~~~~~~~~~~~~ + +Rulesets are evaluated when |TS| completes a transaction, i.e., after a response has been sent to the client. Therefore, header modifications at this hook condition only makes sense for internal headers. + Affected Conditions ------------------- diff --git a/doc/developer-guide/api/functions/TSHttpTxnServerSsnTransactionCount.en.rst b/doc/developer-guide/api/functions/TSHttpTxnServerSsnTransactionCount.en.rst new file mode 100644 index 00000000000..3035ea74172 --- /dev/null +++ b/doc/developer-guide/api/functions/TSHttpTxnServerSsnTransactionCount.en.rst @@ -0,0 +1,37 @@ +.. 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:: ../../../common.defs + +.. default-domain:: c + +TSHttpTxnServerSsnTransactionCount +********************************** + +Synopsis +======== + +.. code-block:: cpp + + #include + +.. function:: int TSHttpTxnServerSsnTransactionCount(TSHttpTxn txnp) + +Description +=========== + +Gets the number of transactions between the Traffic Server proxy and the origin server from a single session. +Any value greater than zero indicates connection reuse. diff --git a/include/ts/ts.h b/include/ts/ts.h index 3db56376fb2..431d6e062b8 100644 --- a/include/ts/ts.h +++ b/include/ts/ts.h @@ -1261,6 +1261,15 @@ tsapi TSReturnCode TSHttpTxnCachedRespGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLo tsapi TSReturnCode TSHttpTxnPristineUrlGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *url_loc); +/** + * @brief Gets the number of transactions between the Traffic Server proxy and the origin server from a single session. + * Any value greater than zero indicates connection reuse. + * + * @param txnp The transaction + * @return int The number of transactions between the Traffic Server proxy and the origin server from a single session + */ +tsapi int TSHttpTxnServerSsnTransactionCount(TSHttpTxn txnp); + /** Get the effective URL for the transaction. The effective URL is the URL taking in to account both the explicit URL in the request and the HOST field. diff --git a/plugins/header_rewrite/conditions.cc b/plugins/header_rewrite/conditions.cc index b42c9f91564..64da6039273 100644 --- a/plugins/header_rewrite/conditions.cc +++ b/plugins/header_rewrite/conditions.cc @@ -33,6 +33,17 @@ #include "expander.h" #include "lulu.h" +// This is a bit of a hack, to get the more linux specific tcp_info struct ... +#if HAVE_STRUCT_LINUX_TCP_INFO +#ifndef _LINUX_TCP_H +#define _LINUX_TCP_H +#endif +#elif HAVE_NETINET_IN_H +#ifndef _NETINET_TCP_H +#define _NETINET_TCP_H +#endif +#endif + // ConditionStatus void ConditionStatus::initialize(Parser &p) @@ -1432,3 +1443,107 @@ ConditionExpandableString::append_value(std::string &s, const Resources &res) s += ve.expand(res); TSDebug(PLUGIN_NAME, "Appending to evaluation value -> %s", s.c_str()); } +// ConditionSessionTransactCount +void +ConditionSessionTransactCount::initialize(Parser &p) +{ + Condition::initialize(p); + MatcherType *match = new MatcherType(_cond_op); + std::string const &arg = p.get_arg(); + + match->set(strtol(arg.c_str(), nullptr, 10)); + _matcher = match; +} + +bool +ConditionSessionTransactCount::eval(const Resources &res) +{ + int const val = TSHttpTxnServerSsnTransactionCount(res.txnp); + + TSDebug(PLUGIN_NAME, "Evaluating SSN-TXN-COUNT()"); + return static_cast(_matcher)->test(val); +} + +void +ConditionSessionTransactCount::append_value(std::string &s, Resources const &res) +{ + char value[32]; // enough for UINT64_MAX + int const count = TSHttpTxnServerSsnTransactionCount(res.txnp); + int const length = ink_fast_itoa(count, value, sizeof(value)); + + if (length > 0) { + TSDebug(PLUGIN_NAME, "Appending SSN-TXN-COUNT %s to evaluation value %.*s", _qualifier.c_str(), length, value); + s.append(value, length); + } +} + +void +ConditionTcpInfo::initialize(Parser &p) +{ + Condition::initialize(p); + TSDebug(PLUGIN_NAME, "Initializing TCP Info"); + MatcherType *match = new MatcherType(_cond_op); + std::string const &arg = p.get_arg(); + + match->set(strtol(arg.c_str(), nullptr, 10)); + _matcher = match; +} + +void +ConditionTcpInfo::initialize_hooks() +{ + add_allowed_hook(TS_HTTP_TXN_START_HOOK); + add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK); + add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK); +} + +bool +ConditionTcpInfo::eval(const Resources &res) +{ + std::string s; + + append_value(s, res); + bool rval = static_cast *>(_matcher)->test(s); + + TSDebug(PLUGIN_NAME, "Evaluating TCP-Info: %s - rval: %d", s.c_str(), rval); + + return rval; +} + +void +ConditionTcpInfo::append_value(std::string &s, Resources const &res) +{ +#if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO) + if (TSHttpTxnIsInternal(res.txnp)) { + TSDebug(PLUGIN_NAME, "No TCP-INFO available for internal transactions"); + return; + } + TSReturnCode tsSsn; + int fd; + struct tcp_info info; + socklen_t tcp_info_len = sizeof(info); + tsSsn = TSHttpTxnClientFdGet(res.txnp, &fd); + if (tsSsn != TS_SUCCESS || fd <= 0) { + TSDebug(PLUGIN_NAME, "error getting the client socket fd from ssn"); + } + if (getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tcp_info_len) != 0) { + TSDebug(PLUGIN_NAME, "getsockopt(%d, TCP_INFO) failed: %s", fd, strerror(errno)); + } + + if (tsSsn == TS_SUCCESS) { + if (tcp_info_len > 0) { + char buf[12 * 4 + 9]; // 4x uint32's + 4x "; " + '\0' +#if !defined(freebsd) || defined(__GLIBC__) + snprintf(buf, sizeof(buf), "%" PRIu32 ";%" PRIu32 ";%" PRIu32 ";%" PRIu32 "", info.tcpi_rtt, info.tcpi_rto, + info.tcpi_snd_cwnd, info.tcpi_retrans); +#else + snprintf(buf, sizeof(buf), "%" PRIu32 ";%" PRIu32 ";%" PRIu32 ";%" PRIu32 "", info.tcpi_rtt, info.tcpi_rto, + info.tcpi_snd_cwnd, info.__tcpi_retrans); +#endif + s += buf; + } + } +#else + s += "-"; +#endif +} diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h index a9bb92ff4c6..16bfe51d25e 100644 --- a/plugins/header_rewrite/conditions.h +++ b/plugins/header_rewrite/conditions.h @@ -575,3 +575,42 @@ class ConditionExpandableString : public Condition std::string _value; DISALLOW_COPY_AND_ASSIGN(ConditionExpandableString); }; + +// Single Session Transaction Count +class ConditionSessionTransactCount : public Condition +{ + typedef Matchers MatcherType; + +public: + ConditionSessionTransactCount() { TSDebug(PLUGIN_NAME_DBG, "ConditionSessionTransactCount()"); } + + // noncopyable + ConditionSessionTransactCount(const ConditionSessionTransactCount &) = delete; + void operator=(const ConditionSessionTransactCount &) = delete; + + void initialize(Parser &p) override; + void append_value(std::string &s, const Resources &res) override; + +protected: + bool eval(const Resources &res) override; +}; + +// Tcp Info +class ConditionTcpInfo : public Condition +{ + typedef Matchers MatcherType; + +public: + ConditionTcpInfo() { TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for ConditionTcpInfo"); } + + // noncopyable + ConditionTcpInfo(const ConditionTcpInfo &) = delete; + void operator=(const ConditionTcpInfo &) = delete; + + void initialize(Parser &p) override; + void append_value(std::string &s, const Resources &res) override; + +protected: + bool eval(const Resources &res) override; + void initialize_hooks() override; // Return status only valid in certain hooks +}; diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc index 028fc6289ce..7003d43fdcd 100644 --- a/plugins/header_rewrite/factory.cc +++ b/plugins/header_rewrite/factory.cc @@ -143,6 +143,10 @@ condition_factory(const std::string &cond) c = new ConditionCidr(); } else if (c_name == "INBOUND") { c = new ConditionInbound(); + } else if (c_name == "SSN-TXN-COUNT") { + c = new ConditionSessionTransactCount(); + } else if (c_name == "TCP-INFO") { + c = new ConditionTcpInfo(); } else { TSError("[%s] Unknown condition %s", PLUGIN_NAME, c_name.c_str()); return nullptr; diff --git a/plugins/header_rewrite/header_rewrite.cc b/plugins/header_rewrite/header_rewrite.cc index f49728acd70..c6b68a69814 100644 --- a/plugins/header_rewrite/header_rewrite.cc +++ b/plugins/header_rewrite/header_rewrite.cc @@ -281,6 +281,12 @@ cont_rewrite_headers(TSCont contp, TSEvent event, void *edata) case TS_EVENT_HTTP_SEND_RESPONSE_HDR: hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK; break; + case TS_EVENT_HTTP_TXN_START: + hook = TS_HTTP_TXN_START_HOOK; + break; + case TS_EVENT_HTTP_TXN_CLOSE: + hook = TS_HTTP_TXN_CLOSE_HOOK; + break; default: TSError("[%s] unknown event for this plugin", PLUGIN_NAME); TSDebug(PLUGIN_NAME, "unknown event for this plugin"); diff --git a/plugins/header_rewrite/parser.cc b/plugins/header_rewrite/parser.cc index 29594273433..e3d617964b4 100644 --- a/plugins/header_rewrite/parser.cc +++ b/plugins/header_rewrite/parser.cc @@ -244,6 +244,14 @@ Parser::cond_is_hook(TSHttpHookID &hook) const hook = TS_REMAP_PSEUDO_HOOK; return true; } + if ("TXN_START_HOOK" == _op) { + hook = TS_HTTP_TXN_START_HOOK; + return true; + } + if ("TXN_CLOSE_HOOK" == _op) { + hook = TS_HTTP_TXN_CLOSE_HOOK; + return true; + } return false; } diff --git a/plugins/header_rewrite/resources.cc b/plugins/header_rewrite/resources.cc index cc1379e93b4..560ce16d7e7 100644 --- a/plugins/header_rewrite/resources.cc +++ b/plugins/header_rewrite/resources.cc @@ -99,6 +99,24 @@ Resources::gather(const ResourceIDs ids, TSHttpHookID hook) } break; + case TS_HTTP_TXN_START_HOOK: + // Get TCP Info at transaction start + if (client_bufp && client_hdr_loc) { + TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers for TXN Start instance"); + bufp = client_bufp; + hdr_loc = client_hdr_loc; + } + break; + + case TS_HTTP_TXN_CLOSE_HOOK: + // Get TCP Info at transaction close + TSDebug(PLUGIN_NAME, "\tAdding TXN close buffers"); + if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) { + TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request"); + return; + } + break; + default: break; } diff --git a/plugins/header_rewrite/statement.cc b/plugins/header_rewrite/statement.cc index ee342dc8adc..fd2c17f40a7 100644 --- a/plugins/header_rewrite/statement.cc +++ b/plugins/header_rewrite/statement.cc @@ -69,6 +69,8 @@ Statement::initialize_hooks() add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK); add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK); add_allowed_hook(TS_REMAP_PSEUDO_HOOK); + add_allowed_hook(TS_HTTP_TXN_START_HOOK); + add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK); } // Parse URL qualifiers, this one is special since it's used in a few places. diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc index b574f1d31e7..5199db474cb 100644 --- a/src/traffic_server/InkAPI.cc +++ b/src/traffic_server/InkAPI.cc @@ -4764,6 +4764,16 @@ TSHttpTxnPristineUrlGet(TSHttpTxn txnp, TSMBuffer *bufp, TSMLoc *url_loc) return TS_ERROR; } +int +TSHttpTxnServerSsnTransactionCount(TSHttpTxn txnp) +{ + sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS); + + HttpSM *sm = (HttpSM *)txnp; + // Any value greater than zero indicates connection reuse. + return sm->server_transact_count; +} + // Shortcut to just get the URL. // The caller is responsible to free memory that is allocated for the string // that is returned. diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc index 5b5f6cd5eaf..b56fd553f8a 100644 --- a/src/traffic_server/InkAPITest.cc +++ b/src/traffic_server/InkAPITest.cc @@ -3057,6 +3057,7 @@ REGRESSION_TEST(SDK_API_TSContSchedule)(RegressionTest *test, int /* atype ATS_U // TSHttpTxnNextHopAddrGet // TSHttpTxnClientProtocolStackGet // TSHttpTxnClientProtocolStackContains +// TSHttpTxnServerSsnTransactionCount ////////////////////////////////////////////////////////////////////////////// #define HTTP_HOOK_TEST_REQUEST_ID 1 @@ -3398,6 +3399,22 @@ checkHttpTxnServerRespGet(SocketTest *test, void *data) return TS_EVENT_CONTINUE; } +// This func is called by us from mytest_handler to test TSHttpTxnServerSsnTransactionCount +static int +checkHttpTxnServerSsnTransactionCount(SocketTest *test, void *data) +{ + TSHttpTxn txnp = (TSHttpTxn)data; + + int count = TSHttpTxnServerSsnTransactionCount(txnp); + if (count < 0) { + SDK_RPRINT(test->regtest, "TSHttpTxnServerSsnTransactionCount", "TestCase1", TC_FAIL, "invalid count value '%d'", count); + } else { + SDK_RPRINT(test->regtest, "TSHttpTxnServerSsnTransactionCount", "TestCase1", TC_PASS, "ok - count='%d'", count); + } + + return count; +} + // This func is called both by us when scheduling EVENT_IMMEDIATE // And by HTTP SM for registered hooks // Depending on the timing of the DNS response, OS_DNS can happen before or after CACHE_LOOKUP. @@ -3478,6 +3495,7 @@ mytest_handler(TSCont contp, TSEvent event, void *data) test->hook_mask |= 32; } checkHttpTxnServerRespGet(test, data); + checkHttpTxnServerSsnTransactionCount(test, data); TSHttpTxnReenable((TSHttpTxn)data, TS_EVENT_HTTP_CONTINUE); test->reenable_mask |= 32; @@ -3490,7 +3508,7 @@ mytest_handler(TSCont contp, TSEvent event, void *data) checkHttpTxnClientRespGet(test, data); - TSHttpTxnReenable((TSHttpTxn)data, TS_EVENT_HTTP_CONTINUE); + TSHttpTxnReenable(static_cast(data), TS_EVENT_HTTP_CONTINUE); test->reenable_mask |= 64; break; diff --git a/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_cond_ssn_txn_count.gold b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_cond_ssn_txn_count.gold new file mode 100644 index 00000000000..482d736e137 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/gold/header_rewrite_cond_ssn_txn_count.gold @@ -0,0 +1,71 @@ +`` +> GET /hello HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/`` +> Accept: */* +> Connection: keep-alive +`` +< HTTP/1.1 200 OK +< Server: ATS/`` +< Content-Length: ``0`` +< Date: `` +< Age: `` +< Connection: keep-alive +< +`` +> GET /hello HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/`` +> Accept: */* +> Connection: keep-alive +`` +< HTTP/1.1 200 OK +< Server: ATS/`` +< Content-Length: ``0`` +< Date: `` +< Age: `` +< Connection: keep-alive +< +`` +> GET /hello HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/`` +> Accept: */* +> Connection: keep-alive +`` +< HTTP/1.1 200 OK +< Server: ATS/`` +< Content-Length: ``0`` +< Date: `` +< Age: `` +< Connection: keep-alive +< +`` +> GET /hello HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/`` +> Accept: */* +> Connection: keep-alive +`` +< HTTP/1.1 200 OK +< Server: ATS/`` +< Content-Length: ``0`` +< Date: `` +< Age: `` +< Connection: close +< +`` +> GET /world HTTP/1.1 +> Host: www.example.com +> User-Agent: curl/`` +> Accept: */* +> Connection: close +`` +< HTTP/1.1 200 OK +< Server: ATS/`` +< Content-Length: ``0`` +< Date: `` +< Age: `` +< Connection: close +< +`` \ No newline at end of file diff --git a/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_cond_ssn_txn_count.test.py b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_cond_ssn_txn_count.test.py new file mode 100644 index 00000000000..73dd6b657fd --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/header_rewrite_cond_ssn_txn_count.test.py @@ -0,0 +1,90 @@ +''' +Test adding a close connection header when SSN-TXN-COUNT reach a limit +''' +# 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. + +ts = Test.MakeATSProcess("ts") +server = Test.MakeOriginServer("server") + +Test.testName = "SSN-TXN-COUNT" + +# Test SSN-TXN-COUNT condition. +request_header_hello = {"headers": + "GET /hello HTTP/1.1\r\nHost: www.example.com\r\nContent-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} +response_header_hello = {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\n" + "Content-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} + +request_header_world = {"headers": "GET /world HTTP/1.1\r\nContent-Length: 0\r\n" + "Host: www.example.com\r\n\r\n", + "timestamp": "1469733493.993", "body": "a\r\na\r\na\r\n\r\n"} +response_header_world = {"headers": "HTTP/1.1 200 OK\r\nServer: microserver\r\n" + "Connection: close\r\nContent-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", "body": ""} + +# add request/response +server.addResponse("sessionlog.log", request_header_hello, response_header_hello) +server.addResponse("sessionlog.log", request_header_world, response_header_world) + +ts.Disk.records_config.update({ + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'header.*', + 'proxy.config.http.auth_server_session_private': 1, + 'proxy.config.http.server_session_sharing.pool': 'global', + 'proxy.config.http.server_session_sharing.match': 'both', +}) + +# In case we need this in the future, just remove the comments. +# ts.Disk.logging_yaml.AddLines( +# ''' +# logging: +# formats: +# - name: long +# format: "SSTC:%" +# logs: +# - filename: rewrite.log.txt +# format: long +# '''.split("\n") +# ) + +# This rule adds the connection header to close it after the number of transactions from a single +# session is > 2. This test the new SSN-TXN-COUNT condition. +ts.Setup.CopyAs('rules/rule_set_header_after_ssn_txn_count.conf', Test.RunDirectory) + +ts.Disk.remap_config.AddLine( + 'map / http://127.0.0.1:{0} @plugin=header_rewrite.so @pparam={1}/rule_set_header_after_ssn_txn_count.conf'.format( + server.Variables.Port, Test.RunDirectory)) + +curlRequest = ( + 'curl -v -H\'Host: www.example.com\' -H\'Connection: keep-alive\' http://127.0.0.1:{0}/hello &&' + 'curl -v -H\'Host: www.example.com\' -H\'Connection: keep-alive\' http://127.0.0.1:{0}/hello &&' + 'curl -v -H\'Host: www.example.com\' -H\'Connection: keep-alive\' http://127.0.0.1:{0}/hello &&' + 'curl -v -H\'Host: www.example.com\' -H\'Connection: keep-alive\' http://127.0.0.1:{0}/hello &&' + # I have to force last one with close connection header, this is also reflected in the response ^. + # if I do not do this, then the microserver will fail to close and when shutting down the process will + # fail with -9. + 'curl -v -H\'Host: www.example.com\' -H\'Connection: close\' http://127.0.0.1:{0}/world' +) + +tr = Test.AddTestRun("Add connection close header when ssn-txn-count > 2") +tr.Processes.Default.Command = curlRequest.format(ts.Variables.port) +tr.Processes.Default.ReturnCode = 0 +tr.Processes.Default.StartBefore(server) +tr.Processes.Default.StartBefore(ts) +tr.Processes.Default.Streams.stderr = "gold/header_rewrite_cond_ssn_txn_count.gold" +tr.StillRunningAfter = server diff --git a/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf new file mode 100644 index 00000000000..ab3fdd36536 --- /dev/null +++ b/tests/gold_tests/pluginTest/header_rewrite/rules/rule_set_header_after_ssn_txn_count.conf @@ -0,0 +1,19 @@ +# +# 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 %{SEND_RESPONSE_HDR_HOOK} [AND] + cond %{SSN-TXN-COUNT} >2 + set-header Connection close \ No newline at end of file