Skip to content

header_rewrite: Add set-plugin-cntl #12125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
16 changes: 16 additions & 0 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,22 @@ TXN_DEBUG Enable transaction debugging (default: ``off``)
SKIP_REMAP Don't require a remap match for the transaction (default: ``off``)
================ ====================================================================

set-plugin-cntl
~~~~~~~~~~~~~~~
::

set-plugin-cntl <controller> <value>

This operator lets you control the fundamental behavior of this plugin for a particular transaction.
The available controllers are:

================== ===================== =============================================================================================
Controller Operators/Conditions Description
================== ===================== =============================================================================================
TIMEZONE ``NOW`` If ``GMT`` is passed, the operators and conditions use GMT regardles of the timezone setting
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOW is not a valid timezone, maybe put LOCAL there instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOW is not a default value. There are three columns and the second column shows the operators and conditions that are controlled by a controller.

on your system. The default value is ``LOCAL``.
================== ===================== =============================================================================================

Operator Flags
--------------

Expand Down
19 changes: 13 additions & 6 deletions plugins/header_rewrite/conditions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ ConditionTransactCount::append_value(std::string &s, Resources const &res)
// Time related functionality for statements. We return an int64_t here, to assure that
// gettimeofday() / Epoch does not lose bits.
int64_t
ConditionNow::get_now_qualified(NowQualifiers qual) const
ConditionNow::get_now_qualified(NowQualifiers qual, const Resources &resources) const
{
time_t now;

Expand All @@ -670,7 +670,14 @@ ConditionNow::get_now_qualified(NowQualifiers qual) const
} else {
struct tm res;

localtime_r(&now, &res);
PrivateSlotData private_data;
private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(resources.txnp, _txn_private_slot));
if (private_data.timezone == 1) {
gmtime_r(&now, &res);
} else {
localtime_r(&now, &res);
}

switch (qual) {
case NOW_QUAL_YEAR:
return static_cast<int64_t>(res.tm_year + 1900); // This makes more sense
Expand Down Expand Up @@ -741,16 +748,16 @@ ConditionNow::set_qualifier(const std::string &q)
}

void
ConditionNow::append_value(std::string &s, const Resources & /* res ATS_UNUSED */)
ConditionNow::append_value(std::string &s, const Resources &res)
{
s += std::to_string(get_now_qualified(_now_qual));
s += std::to_string(get_now_qualified(_now_qual, res));
Dbg(pi_dbg_ctl, "Appending NOW() to evaluation value -> %s", s.c_str());
}

bool
ConditionNow::eval(const Resources & /* res ATS_UNUSED */)
ConditionNow::eval(const Resources &res)
{
int64_t now = get_now_qualified(_now_qual);
int64_t now = get_now_qualified(_now_qual, res);

Dbg(pi_dbg_ctl, "Evaluating NOW()");
return static_cast<const MatcherType *>(_matcher)->test(now);
Expand Down
8 changes: 7 additions & 1 deletion plugins/header_rewrite/conditions.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,14 @@ class ConditionNow : public Condition
protected:
bool eval(const Resources &res) override;

bool
need_txn_private_slot() const override
{
return true;
}

private:
int64_t get_now_qualified(NowQualifiers qual) const;
int64_t get_now_qualified(NowQualifiers qual, const Resources &res) const;
NowQualifiers _now_qual = NOW_QUAL_EPOCH;
};

Expand Down
2 changes: 2 additions & 0 deletions plugins/header_rewrite/factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ operator_factory(const std::string &op)
o = new OperatorSetBody();
} else if (op == "set-http-cntl") {
o = new OperatorSetHttpCntl();
} else if (op == "set-plugin-cntl") {
o = new OperatorSetPluginCntl();
} else if (op == "run-plugin") {
o = new OperatorRunPlugin();
} else if (op == "set-body-from") {
Expand Down
51 changes: 51 additions & 0 deletions plugins/header_rewrite/operators.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,57 @@ OperatorSetHttpCntl::exec(const Resources &res) const
return true;
}

void
OperatorSetPluginCntl::initialize(Parser &p)
{
Operator::initialize(p);
const std::string &name = p.get_arg();
const std::string &value = p.get_value();

if (name == "TIMEZONE") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOCAL and GMT look like the only two possible values, should add that to the docs.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's already written on the docs.

_name = PluginCtrl::TIMEZONE;
if (value == "LOCAL") {
_value = TIMEZONE_LOCAL;
} else if (value == "GMT") {
_value = TIMEZONE_GMT;
} else {
TSError("[%s] Unknown value for TIMZEONE control: %s", PLUGIN_NAME, value.c_str());
}
}
}

// This operator should be allowed everywhere
void
OperatorSetPluginCntl::initialize_hooks()
{
add_allowed_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
add_allowed_hook(TS_REMAP_PSEUDO_HOOK);
add_allowed_hook(TS_HTTP_PRE_REMAP_HOOK);
add_allowed_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
add_allowed_hook(TS_HTTP_TXN_CLOSE_HOOK);
add_allowed_hook(TS_HTTP_TXN_START_HOOK);
}

bool
OperatorSetPluginCntl::exec(const Resources &res) const
{
PrivateSlotData private_data;
private_data.raw = reinterpret_cast<uint64_t>(TSUserArgGet(res.txnp, _txn_private_slot));

switch (_name) {
case PluginCtrl::TIMEZONE:
private_data.timezone = _value;
break;
}

Dbg(pi_dbg_ctl, " Setting plugin control %d to %d", static_cast<int>(_name), _value);
TSUserArgSet(res.txnp, _txn_private_slot, reinterpret_cast<void *>(private_data.raw));

return true;
}

void
OperatorRunPlugin::initialize(Parser &p)
{
Expand Down
30 changes: 30 additions & 0 deletions plugins/header_rewrite/operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,36 @@ class OperatorSetHttpCntl : public Operator
TSHttpCntlType _cntl_qual;
};

class OperatorSetPluginCntl : public Operator
{
public:
OperatorSetPluginCntl() { Dbg(dbg_ctl, "Calling CTOR for OperatorSetPluginCntl"); }

// noncopyable
OperatorSetPluginCntl(const OperatorSetPluginCntl &) = delete;
void operator=(const OperatorSetPluginCntl &) = delete;

void initialize(Parser &p) override;

enum class PluginCtrl {
TIMEZONE,
};

protected:
void initialize_hooks() override;
bool exec(const Resources &res) const override;

bool
need_txn_private_slot() const override
{
return true;
}

private:
PluginCtrl _name;
int _value;
};

class RemapPluginInst; // Opaque to the HRW operator, but needed in the implementation.

class OperatorRunPlugin : public Operator
Expand Down
22 changes: 22 additions & 0 deletions plugins/header_rewrite/statement.cc
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,28 @@ Statement::acquire_txn_slot()
_txn_slot = txn_slot_index;
}

void
Statement::acquire_txn_private_slot()
{
// Don't do anything if we don't need it
if (!need_txn_private_slot() || _txn_private_slot >= 0) {
return;
}

// Only call the index reservation once per plugin load
static int txn_private_slot_index = []() -> int {
int index = -1;

if (TS_ERROR == TSUserArgIndexReserve(TS_USER_ARGS_TXN, PLUGIN_NAME, "HRW txn private variables", &index)) {
TSError("[%s] failed to reserve user arg index", PLUGIN_NAME);
return -1; // Fallback value
}
return index;
}();

_txn_private_slot = txn_private_slot_index;
}

// Parse NextHop qualifiers
NextHopQualifiers
Statement::parse_next_hop_qualifier(const std::string &q) const
Expand Down
22 changes: 20 additions & 2 deletions plugins/header_rewrite/statement.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ class Statement
TSReleaseAssert(_initialized == false);
initialize_hooks();
acquire_txn_slot();
acquire_txn_private_slot();

_initialized = true;
}
Expand Down Expand Up @@ -184,14 +185,31 @@ class Statement
return false;
}

Statement *_next = nullptr; // Linked list
int _txn_slot = -1;
virtual bool
need_txn_private_slot() const
{
return false;
}

Statement *_next = nullptr; // Linked list
int _txn_slot = -1;
int _txn_private_slot = -1;

private:
void acquire_txn_slot();
void acquire_txn_private_slot();

ResourceIDs _rsrc = RSRC_NONE;
TSHttpHookID _hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
std::vector<TSHttpHookID> _allowed_hooks;
bool _initialized = false;
};

union PrivateSlotData {
uint64_t raw;
struct {
uint64_t timezone : 1; // TIMEZONE_LOCAL, or TIMEZONE_GMT
};
};

enum { TIMEZONE_LOCAL, TIMEZONE_GMT };