Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
------------------

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 9 additions & 0 deletions include/ts/ts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
34 changes: 34 additions & 0 deletions plugins/header_rewrite/conditions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
19 changes: 19 additions & 0 deletions plugins/header_rewrite/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -544,3 +544,22 @@ class ConditionStringLiteral : public Condition
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;
};
2 changes: 2 additions & 0 deletions plugins/header_rewrite/factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions src/traffic_server/InkAPI.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 18 additions & 1 deletion src/traffic_server/InkAPITest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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
<
``
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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