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
18 changes: 18 additions & 0 deletions doc/admin-guide/plugins/header_rewrite.en.rst
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,14 @@ changing the remapped destination, ``<part>`` should be used to indicate the
component that is being modified (see `URL Parts`_). Currently the only valid
parts for rm-destination are QUERY, PATH, and PORT.

run-plugin
~~~~~~~~~~~~~~
::

run-plugin <plugin-name>.so "<plugin-argument> ..."

This allows to run an existing remap plugin, conditionally, from within a
header rewrite rule.
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this run on any hook other than the remap hook?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No it can only run in a remap hook, but the plugin it runs can setup other hooks.


set-header
~~~~~~~~~~
Expand Down Expand Up @@ -1350,3 +1358,13 @@ the client where the requested data was served from.::
cond %{HEADER:ATS-SRVR-UUID} ="" [OR]
cond %{CACHE} ="hit-fresh"
set-header ATS-SRVR-UUID %{ID:UNIQUE}

Apply rate limiting for some select requests
------------------------------------

This rule will conditiionally, based on the client request headers, apply rate
limiting to the request.::

cond %{REMAP_PSEUDO_HOOK} [AND]
cond %{CLIENT-HEADER:Some-Special-Header} ="yes"
run-plugin rate_limit.so "--limit=300 --error=429"
4 changes: 2 additions & 2 deletions plugins/header_rewrite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ target_link_libraries(header_rewrite_parser PUBLIC libswoc::libswoc)

target_link_libraries(
header_rewrite
PRIVATE PCRE::PCRE
PRIVATE ts::tscore PCRE::PCRE
PUBLIC libswoc::libswoc
)

Expand All @@ -52,7 +52,7 @@ if(BUILD_TESTING)
add_executable(test_header_rewrite header_rewrite_test.cc)
add_test(NAME test_header_rewrite COMMAND $<TARGET_FILE:test_header_rewrite>)

target_link_libraries(test_header_rewrite PRIVATE header_rewrite_parser)
target_link_libraries(test_header_rewrite PRIVATE header_rewrite_parser ts::inkevent ts::tscore)

if(maxminddb_FOUND)
target_link_libraries(test_header_rewrite PRIVATE maxminddb::maxminddb)
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 == "run-plugin") {
o = new OperatorRunPlugin();

} else {
TSError("[%s] Unknown operator: %s", PLUGIN_NAME, op.c_str());
Expand Down
34 changes: 24 additions & 10 deletions plugins/header_rewrite/header_rewrite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "ts/remap.h"
#include "ts/remap_version.h"

#include "proxy/http/remap/PluginFactory.h"

#include "parser.h"
#include "ruleset.h"
#include "resources.h"
Expand All @@ -35,19 +37,27 @@
// Debugs
const char PLUGIN_NAME[] = "header_rewrite";
const char PLUGIN_NAME_DBG[] = "dbg_header_rewrite";

namespace header_rewrite_ns
{
DbgCtl dbg_ctl{PLUGIN_NAME_DBG};
DbgCtl pi_dbg_ctl{PLUGIN_NAME};

PluginFactory plugin_factory;
} // namespace header_rewrite_ns

static std::once_flag initGeoLibs;
static std::once_flag initHRWLibs;

static void
initGeoLib(const std::string &dbPath)
initHRWLibraries(const std::string &dbPath)
{
header_rewrite_ns::plugin_factory.setRuntimeDir(RecConfigReadRuntimeDir()).addSearchDir(RecConfigReadPluginDir());

if (dbPath.empty()) {
return;
}

Dbg(pi_dbg_ctl, "Loading geo db %s", dbPath.c_str());

#if TS_USE_HRW_GEOIP
GeoIPConditionGeo::initLibrary(dbPath);
#elif TS_USE_HRW_MAXMINDDB
Expand Down Expand Up @@ -99,7 +109,7 @@ class RulesConfig
return _rules[hook];
}

bool parse_config(const std::string &fname, TSHttpHookID default_hook);
bool parse_config(const std::string &fname, TSHttpHookID default_hook, char *from_url = nullptr, char *to_url = nullptr);

private:
bool add_rule(RuleSet *rule);
Expand Down Expand Up @@ -133,7 +143,7 @@ RulesConfig::add_rule(RuleSet *rule)
// anyways (or reload for remap.config), so not really in the critical path.
//
bool
RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook)
RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook, char *from_url, char *to_url)
{
RuleSet *rule = nullptr;
std::string filename;
Expand Down Expand Up @@ -177,7 +187,7 @@ RulesConfig::parse_config(const std::string &fname, TSHttpHookID default_hook)
continue;
}

Parser p;
Parser p(from_url, to_url);

// Tokenize and parse this line
if (!p.parse_line(line)) {
Expand Down Expand Up @@ -356,7 +366,7 @@ TSPluginInit(int argc, const char *argv[])

Dbg(pi_dbg_ctl, "Global geo db %s", geoDBpath.c_str());

std::call_once(initGeoLibs, [&geoDBpath]() { initGeoLib(geoDBpath); });
std::call_once(initHRWLibs, [&geoDBpath]() { initHRWLibraries(geoDBpath); });

// Parse the global config file(s). All rules are just appended
// to the "global" Rules configuration.
Expand Down Expand Up @@ -414,6 +424,9 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
return TS_ERROR;
}

char *from_url = argv[0];
char *to_url = argv[1];

// argv contains the "to" and "from" URLs. Skip the first so that the
// second one poses as the program name.
--argc;
Expand All @@ -439,15 +452,15 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
}

Dbg(pi_dbg_ctl, "Remap geo db %s", geoDBpath.c_str());

std::call_once(initGeoLibs, [&geoDBpath]() { initGeoLib(geoDBpath); });
}

std::call_once(initHRWLibs, [&geoDBpath]() { initHRWLibraries(geoDBpath); });

RulesConfig *conf = new RulesConfig;

for (int i = optind; i < argc; ++i) {
Dbg(pi_dbg_ctl, "Loading remap configuration file %s", argv[i]);
if (!conf->parse_config(argv[i], TS_REMAP_PSEUDO_HOOK)) {
if (!conf->parse_config(argv[i], TS_REMAP_PSEUDO_HOOK, from_url, to_url)) {
TSError("[%s] Unable to create remap instance", PLUGIN_NAME);
delete conf;
return TS_ERROR;
Expand All @@ -473,6 +486,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE
void
TSRemapDeleteInstance(void *ih)
{
Dbg(pi_dbg_ctl, "Deleting RulesConfig");
delete static_cast<RulesConfig *>(ih);
}

Expand Down
7 changes: 5 additions & 2 deletions plugins/header_rewrite/lulu.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
#include "tscore/ink_defs.h"
#include "tscore/ink_platform.h"

#include "proxy/http/remap/PluginFactory.h"

#define TS_REMAP_PSEUDO_HOOK TS_HTTP_LAST_HOOK // Ugly, but use the "last hook" for remap instances.

std::string getIP(sockaddr const *s_sockaddr);
Expand All @@ -38,7 +40,8 @@ extern const char PLUGIN_NAME_DBG[];

namespace header_rewrite_ns
{
extern DbgCtl dbg_ctl;
extern DbgCtl pi_dbg_ctl;
extern DbgCtl dbg_ctl;
extern DbgCtl pi_dbg_ctl;
extern PluginFactory plugin_factory;
} // namespace header_rewrite_ns
using namespace header_rewrite_ns;
2 changes: 2 additions & 0 deletions plugins/header_rewrite/operator.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class Operator : public Statement
public:
Operator() { Dbg(dbg_ctl, "Calling CTOR for Operator"); }

virtual ~Operator() = default; // Very uncommon for an Operator to have a custom DTOR, but happens.

// noncopyable
Operator(const Operator &) = delete;
void operator=(const Operator &) = delete;
Expand Down
73 changes: 73 additions & 0 deletions plugins/header_rewrite/operators.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
#include <arpa/inet.h>
#include <cstring>
#include <algorithm>
#include <iomanip>

#include "ts/ts.h"
#include "swoc/swoc_file.h"

#include "operators.h"
#include "ts/apidefs.h"
Expand Down Expand Up @@ -1092,6 +1094,7 @@ OperatorSetHttpCntl::initialize_hooks()
static const char *const HttpCntls[] = {
"LOGGING", "INTERCEPT_RETRY", "RESP_CACHEABLE", "REQ_CACHEABLE", "SERVER_NO_STORE", "TXN_DEBUG", "SKIP_REMAP",
};

void
OperatorSetHttpCntl::exec(const Resources &res) const
{
Expand All @@ -1103,3 +1106,73 @@ OperatorSetHttpCntl::exec(const Resources &res) const
Dbg(pi_dbg_ctl, " Turning OFF %s for transaction", HttpCntls[static_cast<size_t>(_cntl_qual)]);
}
}

void
OperatorRunPlugin::initialize(Parser &p)
{
Operator::initialize(p);

auto plugin_name = p.get_arg();
auto plugin_args = p.get_value();

if (plugin_name.empty()) {
TSError("[%s] missing plugin name", PLUGIN_NAME);
return;
}

std::vector<std::string> tokens;
std::istringstream iss(plugin_args);
std::string token;

while (iss >> std::quoted(token)) {
tokens.push_back(token);
}

// Create argc and argv
int argc = tokens.size() + 2;
char **argv = new char *[argc];

argv[0] = p.from_url();
argv[1] = p.to_url();

for (int i = 0; i < argc; ++i) {
argv[i + 2] = const_cast<char *>(tokens[i].c_str());
}

std::string error;

// We have to escalate access while loading these plugins, just as done when loading remap.config
{
uint32_t elevate_access = 0;

REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
ElevateAccess access(elevate_access ? ElevateAccess::FILE_PRIVILEGE : 0);

_plugin = plugin_factory.getRemapPlugin(swoc::file::path(plugin_name), argc, const_cast<char **>(argv), error,
isPluginDynamicReloadEnabled());
} // done elevating access

delete[] argv;

if (!_plugin) {
TSError("[%s] Unable to load plugin '%s': %s", PLUGIN_NAME, plugin_name.c_str(), error.c_str());
}
}

void
OperatorRunPlugin::initialize_hooks()
{
add_allowed_hook(TS_REMAP_PSEUDO_HOOK);

require_resources(RSRC_CLIENT_REQUEST_HEADERS); // Need this for the txnp
}

void
OperatorRunPlugin::exec(const Resources &res) const
{
TSReleaseAssert(_plugin != nullptr);

if (res._rri && res.txnp) {
_plugin->doRemap(res.txnp, res._rri);
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious. should we pass on the return status of the doRemap() function somewhere?

Copy link
Contributor

Choose a reason for hiding this comment

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

Now it is just ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeh, so we kinda butchered this a bit in all the refactoring and dealings with "chaining" remap plugins. In most cases, this return code is obsoleted, and we don't' really use it. So, I made a choice to ignore it in HRW, whereas in Cripts, the caller can chose to look at it and do something with it (albeit, I think that's unlikely).

I'm not sure the stop / continue works any more either, but almost no plugin that's in use uses that.

}
}
32 changes: 32 additions & 0 deletions plugins/header_rewrite/operators.h
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,35 @@ class OperatorSetHttpCntl : public Operator
bool _flag = false;
TSHttpCntlType _cntl_qual;
};

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

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

// This one is special, since we have to remove the old plugin from the factory.
~OperatorRunPlugin() override
{
Dbg(dbg_ctl, "Calling DTOR for OperatorRunPlugin");

if (_plugin) {
_plugin->done();
_plugin = nullptr;
}
}

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

void initialize(Parser &p) override;

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

private:
RemapPluginInst *_plugin = nullptr;
};
22 changes: 19 additions & 3 deletions plugins/header_rewrite/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,26 @@
class Parser
{
public:
Parser(){};
Parser() = default; // No from/to URLs for this parser
Parser(char *from_url, char *to_url) : _from_url(from_url), _to_url(to_url) {}

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

// These are not const char *, because, you know, everything else with argv is a char *
char *
from_url() const
{
return _from_url;
}

char *
to_url() const
{
return _to_url;
}

bool
empty() const
{
Expand Down Expand Up @@ -95,8 +109,10 @@ class Parser
private:
bool preprocess(std::vector<std::string> tokens);

bool _cond = false;
bool _empty = false;
bool _cond = false;
bool _empty = false;
char *_from_url = nullptr;
char *_to_url = nullptr;
std::vector<std::string> _mods;
std::string _op;
std::string _arg;
Expand Down