You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by sh...@apache.org on 2021/01/05 15:56:44 UTC

[trafficserver] branch master updated: API - Add new api function TSHttpTxnServerSsnTransactionCount() to retrieve the number of transactions between TS proxy and the origin server from a single session. (#7387)

This is an automated email from the ASF dual-hosted git repository.

shinrich 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 e175606  API - Add new api function TSHttpTxnServerSsnTransactionCount() to retrieve the number of transactions between TS proxy and the origin server from a single session. (#7387)
e175606 is described below

commit e175606bbd89f77400107b97f39cdd460e7c9a7a
Author: Damian Meden <da...@gmail.com>
AuthorDate: Tue Jan 5 15:56:30 2021 +0000

    API - Add new api function TSHttpTxnServerSsnTransactionCount() to retrieve the number of transactions between TS proxy and the origin server from a single session. (#7387)
---
 doc/admin-guide/plugins/header_rewrite.en.rst      |  9 +++
 .../TSHttpTxnServerSsnTransactionCount.en.rst      | 37 +++++++++
 include/ts/ts.h                                    |  9 +++
 plugins/header_rewrite/conditions.cc               | 34 ++++++++
 plugins/header_rewrite/conditions.h                | 19 +++++
 plugins/header_rewrite/factory.cc                  |  2 +
 src/traffic_server/InkAPI.cc                       | 10 +++
 src/traffic_server/InkAPITest.cc                   | 19 ++++-
 .../gold/header_rewrite_cond_ssn_txn_count.gold    | 71 +++++++++++++++++
 .../header_rewrite_cond_ssn_txn_count.test.py      | 90 ++++++++++++++++++++++
 .../rules/rule_set_header_after_ssn_txn_count.conf | 19 +++++
 11 files changed, 318 insertions(+), 1 deletion(-)

diff --git a/doc/admin-guide/plugins/header_rewrite.en.rst b/doc/admin-guide/plugins/header_rewrite.en.rst
index 1bc8935..9db100a 100644
--- a/doc/admin-guide/plugins/header_rewrite.en.rst
+++ b/doc/admin-guide/plugins/header_rewrite.en.rst
@@ -482,6 +482,15 @@ Refer to `Requests vs. Responses`_ for more information on determining the
 context in which the transaction's URL is evaluated.  The ``<part>`` may be
 specified according to the options documented in `URL Parts`_.
 
+SSN-TXN-COUNT
+~~~~~~~~~~~~~
+::
+
+    cond %{SSN-TXN-COUNT} <operand>
+
+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.
+
 Condition Operands
 ------------------
 
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 0000000..3035ea7
--- /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 <ts/ts.h>
+
+.. 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 4c4565e..082eda1 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -1313,6 +1313,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 b9df7b8..84ce254 100644
--- a/plugins/header_rewrite/conditions.cc
+++ b/plugins/header_rewrite/conditions.cc
@@ -1300,3 +1300,37 @@ ConditionStringLiteral::eval(const Resources &res)
 
   return static_cast<const MatcherType *>(_matcher)->test(_literal);
 }
+
+// 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<MatcherType *>(_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);
+  }
+}
\ No newline at end of file
diff --git a/plugins/header_rewrite/conditions.h b/plugins/header_rewrite/conditions.h
index 927c823..03f42d4 100644
--- a/plugins/header_rewrite/conditions.h
+++ b/plugins/header_rewrite/conditions.h
@@ -544,3 +544,22 @@ protected:
 private:
   std::string _literal;
 };
+
+// Single Session Transaction Count
+class ConditionSessionTransactCount : public Condition
+{
+  typedef Matchers<int> 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;
+};
\ No newline at end of file
diff --git a/plugins/header_rewrite/factory.cc b/plugins/header_rewrite/factory.cc
index 0d314e4..a5decc7 100644
--- a/plugins/header_rewrite/factory.cc
+++ b/plugins/header_rewrite/factory.cc
@@ -137,6 +137,8 @@ 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 {
     TSError("[%s] Unknown condition %s", PLUGIN_NAME, c_name.c_str());
     return nullptr;
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index 178fe15..5f529c2 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -5093,6 +5093,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 d8211df..f864830 100644
--- a/src/traffic_server/InkAPITest.cc
+++ b/src/traffic_server/InkAPITest.cc
@@ -3034,6 +3034,7 @@ REGRESSION_TEST(SDK_API_TSContSchedule)(RegressionTest *test, int /* atype ATS_U
 //                    TSHttpTxnNextHopAddrGet
 //                    TSHttpTxnClientProtocolStackGet
 //                    TSHttpTxnClientProtocolStackContains
+//                    TSHttpTxnServerSsnTransactionCount
 //////////////////////////////////////////////////////////////////////////////
 
 #define HTTP_HOOK_TEST_REQUEST_ID 1
@@ -3375,6 +3376,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.
@@ -3455,6 +3472,7 @@ mytest_handler(TSCont contp, TSEvent event, void *data)
       test->hook_mask |= 32;
     }
     checkHttpTxnServerRespGet(test, data);
+    checkHttpTxnServerSsnTransactionCount(test, data);
 
     TSHttpTxnReenable(static_cast<TSHttpTxn>(data), TS_EVENT_HTTP_CONTINUE);
     test->reenable_mask |= 32;
@@ -3466,7 +3484,6 @@ mytest_handler(TSCont contp, TSEvent event, void *data)
     }
 
     checkHttpTxnClientRespGet(test, data);
-
     TSHttpTxnReenable(static_cast<TSHttpTxn>(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 0000000..482d736
--- /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 0000000..73dd6b6
--- /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:%<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 0000000..ab3fdd3
--- /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