You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2012/06/13 22:18:14 UTC
[9/11] Moved these plugins from the separate git repo
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/header_rewrite.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/header_rewrite.cc b/plugins/experimental/header_rewrite/header_rewrite.cc
new file mode 100644
index 0000000..aba03ef
--- /dev/null
+++ b/plugins/experimental/header_rewrite/header_rewrite.cc
@@ -0,0 +1,331 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// header_rewrite: YTS plugin to do header rewrites
+// --------------
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__header_rewrite_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <fstream>
+#include <string>
+#include <boost/algorithm/string.hpp>
+
+#include <ts/ts.h>
+#include <ts/remap.h>
+
+#include "parser.h"
+#include "ruleset.h"
+#include "resources.h"
+
+// "Defines"
+static const char* DEFAULT_CONF_PATH = "/usr/local/etc/header_rewrite/";
+
+
+// Global holding the rulesets and resource IDs
+static RuleSet* all_rules[TS_HTTP_LAST_HOOK+1];
+static ResourceIDs all_resids[TS_HTTP_LAST_HOOK+1];
+
+// Helper function to add a rule to the rulesets
+static bool
+add_rule(RuleSet* rule) {
+ if (rule && rule->has_operator()) {
+ TSDebug(PLUGIN_NAME, "Adding rule to hook=%d\n", rule->get_hook());
+ if (NULL == all_rules[rule->get_hook()]) {
+ all_rules[rule->get_hook()] = rule;
+ } else {
+ all_rules[rule->get_hook()]->append(rule);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Simple "config" parser, this modifies the global above. ToDo: What happens
+// on a "config" reload?
+//
+// Note that this isn't particularly efficient, but it's a startup time cost
+// anyways (or reload for remap.config), so not really in the critical path.
+//
+bool
+parse_config(const std::string fname, TSHttpHookID default_hook)
+{
+ RuleSet* rule = NULL;
+ std::string filename = fname;
+ std::ifstream f;
+ int lineno = 0;
+
+ // Try appending the default conf path if the fname doesn't exist.
+ if (0 != access(filename.c_str(), R_OK)) {
+ filename = DEFAULT_CONF_PATH;
+ filename += fname;
+ }
+
+ f.open(filename.c_str(), std::ios::in);
+ if (!f.is_open()) {
+ TSError("header_rewrite: unable to open %s", filename.c_str());
+ return false;
+ }
+
+ TSDebug(PLUGIN_NAME, "Loading header_rewrite config from %s", filename.c_str());
+
+ while (!f.eof()) {
+ std::string line;
+
+ getline(f, line);
+ ++lineno; // ToDo: we should probably use this for error messages ...
+
+ boost::trim(line);
+ if (line.empty() || (line[0] == '#'))
+ continue;
+
+ Parser p(line); // Tokenize and parse this line
+ if (p.empty())
+ continue;
+
+ // If we are at the beginning of a new condition, save away the previous rule (but only if it has operators).
+ if (p.is_cond() && add_rule(rule))
+ rule = NULL;
+
+ if (NULL == rule) {
+ rule = new RuleSet();
+ rule->set_hook(default_hook);
+
+ // Special case for specifying the HOOK this rule applies to.
+ // These can only be at the beginning of a rule, and have an implicit [AND].
+ if (p.cond_op_is("READ_RESPONSE_HDR_HOOK")) {
+ rule->set_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+ continue;
+ } else if (p.cond_op_is("READ_REQUEST_HDR_HOOK")) {
+ rule->set_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+ continue;
+ } else if (p.cond_op_is("READ_REQUEST_PRE_REMAP_HOOK")) {
+ rule->set_hook(TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK);
+ continue;
+ } else if (p.cond_op_is("SEND_REQUEST_HDR_HOOK")) {
+ rule->set_hook(TS_HTTP_SEND_REQUEST_HDR_HOOK);
+ continue;
+ } else if (p.cond_op_is("SEND_RESPONSE_HDR_HOOK")) {
+ rule->set_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+ continue;
+ }
+ }
+
+ if (p.is_cond()) {
+ rule->add_condition(p);
+ } else {
+ rule->add_operator(p);
+ }
+ }
+
+ // Add the last rule (possibly the only rule)
+ add_rule(rule);
+
+ // Collect all resource IDs that we need
+ for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i)
+ if (all_rules[i])
+ all_resids[i] = all_rules[i]->get_all_resource_ids();
+
+ return true;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Continuation
+//
+static int
+cont_rewrite_headers(TSCont contp, TSEvent event, void *edata)
+{
+ TSDebug(PLUGIN_NAME, "plugin: %d", event);
+
+ TSHttpTxn txnp = (TSHttpTxn) edata;
+ Resources res(txnp, contp);
+ const RuleSet* rule = NULL;
+ TSHttpHookID hook = TS_HTTP_LAST_HOOK;
+
+ // Get the resources necessary to process this event
+ switch (event) {
+ case TS_EVENT_HTTP_READ_RESPONSE_HDR:
+ hook = TS_HTTP_READ_RESPONSE_HDR_HOOK;
+ break;
+ case TS_EVENT_HTTP_READ_REQUEST_HDR:
+ hook = TS_HTTP_READ_REQUEST_HDR_HOOK;
+ break;
+ case TS_EVENT_HTTP_READ_REQUEST_PRE_REMAP:
+ hook = TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK;
+ break;
+ case TS_EVENT_HTTP_SEND_REQUEST_HDR:
+ hook = TS_HTTP_SEND_REQUEST_HDR_HOOK;
+ break;
+ case TS_EVENT_HTTP_SEND_RESPONSE_HDR:
+ hook = TS_HTTP_SEND_RESPONSE_HDR_HOOK;
+ break;
+ default:
+ TSError("header_rewrite: unknown event for this plugin");
+ TSDebug(PLUGIN_NAME, "unknown event for this plugin");
+ break;
+ }
+
+ if (hook != TS_HTTP_LAST_HOOK) {
+ res.gather(all_resids[hook], hook);
+ rule = all_rules[hook];
+
+ // Evaluation
+ while (rule) {
+ if (rule->eval(res)) {
+ OperModifiers rt = rule->exec(res);
+
+ if (rule->last() || (rt & OPER_LAST)) {
+ break; // Conditional break, force a break with [L]
+ }
+ }
+ rule = rule->next;
+ }
+ }
+
+ TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the InkAPI plugin for the global hooks we support.
+//
+void
+TSPluginInit(int argc, const char *argv[])
+{
+ TSPluginRegistrationInfo info;
+
+ info.plugin_name = (char*)PLUGIN_NAME;
+ info.vendor_name = (char*)"";
+ info.support_email = (char*)"";
+
+ if (TS_SUCCESS != TSPluginRegister(TS_SDK_VERSION_3_0 , &info)) {
+ TSError("header_rewrite: plugin registration failed.\n");
+ }
+
+ if (argc != 2) {
+ TSError("usage: %s <config-file>\n", argv[0] );
+ }
+
+ // Initialize the globals
+ for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i) {
+ all_rules[i] = NULL;
+ all_resids[i] = RSRC_NONE;
+ }
+
+ // Parse the config file
+ if (parse_config(argv[1], TS_HTTP_READ_RESPONSE_HDR_HOOK)) {
+ for (int i=TS_HTTP_READ_REQUEST_HDR_HOOK; i<TS_HTTP_LAST_HOOK; ++i) {
+ if (all_rules[i]) {
+ TSDebug(PLUGIN_NAME, "adding hook: %d", i);
+ TSHttpHookAdd(static_cast<TSHttpHookID>(i), TSContCreate(cont_rewrite_headers, NULL));
+ }
+ }
+ } else {
+ TSError("header_rewrite: failed to parse configuration file");
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the plugin as a remap plugin.
+//
+TSReturnCode
+TSRemapInit(TSRemapInterface *api_info, char *errbuf, int errbuf_size)
+{
+ if (!api_info) {
+ strncpy(errbuf, "[TSRemapInit] - Invalid TSRemapInterface argument", errbuf_size - 1);
+ return TS_ERROR;
+ }
+
+ if (api_info->size < sizeof(TSRemapInterface)) {
+ strncpy(errbuf, "[TSRemapInit] - Incorrect size of TSRemapInterface structure", errbuf_size - 1);
+ return TS_ERROR;
+ }
+
+ if (api_info->tsremap_version < TSREMAP_VERSION) {
+ snprintf(errbuf, errbuf_size - 1, "[TSRemapInit] - Incorrect API version %ld.%ld",
+ api_info->tsremap_version >> 16, (api_info->tsremap_version & 0xffff));
+ return TS_ERROR;
+ }
+
+ TSDebug(PLUGIN_NAME, "remap plugin is succesfully initialized");
+ return TS_SUCCESS;
+}
+
+
+TSReturnCode
+TSRemapNewInstance(int argc, char *argv[], void **ih, char *errbuf, int errbuf_size)
+{
+ TSDebug(PLUGIN_NAME, "initializing the remap plugin header_rewrite");
+
+ if (argc < 3) {
+ TSError("Unable to create remap instance, need config file");
+ return TS_ERROR;
+ }
+
+ // TODO: this is a big ugly, we use a pseudo hook for parsing the config for a
+ // remap instantiation.
+ all_rules[TS_REMAP_PSEUDO_HOOK] = NULL;
+ if (!parse_config(argv[2], TS_REMAP_PSEUDO_HOOK)) {
+ TSError("Unable to create remap instance");
+ return TS_ERROR;
+ }
+
+ *ih = all_rules[TS_REMAP_PSEUDO_HOOK];
+ all_rules[TS_REMAP_PSEUDO_HOOK] = NULL;
+
+ TSDebug(PLUGIN_NAME, "successfully initialize the header_rewrite plugin");
+ return TS_SUCCESS;
+}
+
+void
+TSRemapDeleteInstance(void *ih)
+{
+ RuleSet* rule = static_cast<RuleSet*>(ih);
+
+ delete rule;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This is the main "entry" point for the plugin, called for every request.
+//
+TSRemapStatus
+TSRemapDoRemap(void *ih, TSHttpTxn rh, TSRemapRequestInfo *rri)
+{
+ TSRemapStatus rval = TSREMAP_NO_REMAP;
+
+ if (NULL == ih) {
+ TSDebug(PLUGIN_NAME, "No Rules configured, falling back to default");
+ return rval;
+ } else {
+ RuleSet* rule = (RuleSet*)ih;
+ Resources res((TSHttpTxn)rh, rri);
+
+ // TODO: This might be suboptimal, do we always need the client request
+ // headers in a remap plugin?
+ res.gather(RSRC_CLIENT_REQUEST_HEADERS, TS_REMAP_PSEUDO_HOOK);
+
+ // Evaluation
+ while (rule) {
+ if (rule->eval(res)) {
+ OperModifiers rt = rule->exec(res);
+ if (res.changed_url == true)
+ rval = TSREMAP_DID_REMAP;
+
+ if (rule->last() || (rt & OPER_LAST)) {
+ break; // Conditional break, force a break with [L]
+ }
+ }
+ rule = rule->next;
+ }
+
+ }
+
+ return rval;
+}
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/header_rewrite.config
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/header_rewrite.config b/plugins/experimental/header_rewrite/header_rewrite.config
new file mode 100644
index 0000000..8be269a
--- /dev/null
+++ b/plugins/experimental/header_rewrite/header_rewrite.config
@@ -0,0 +1,77 @@
+Operators
+---------
+rm-header Header matcher
+add-header Header value
+set-header Header value
+
+set-status value
+set-status-reason value
+
+
+Conditions
+-----------
+%{TRUE} # Default condition if none specified
+%{FALSE}
+%{HEADER:string}
+%{YCOOKIE:id}
+%{METHOD}
+%{PROTOCOL}
+%{PORT}
+%{HOST}
+%{TOHOST}
+%{FROMHOST}
+%{PATH}
+%{PARAMS}
+%{QUERY}
+%{STATUS}
+
+# These can only be used as the first condition of a new ruleset,
+# and have no run-time evaluation effects (only config parse time).
+%{READ_RESPONSE_HDR_HOOK} # Default
+%{READ_REQUEST_HDR_HOOK}
+
+
+Operator Flags
+--------------
+L # Last rule (stop evaluations)
+
+
+Cond Flags
+----------
+NC # Not case sensitive condition (when applicable)
+NOT # Negate the cond
+OR # Local OR between conds
+AND # Logical AND betwen conds
+
+
+Matcher
+--------
+
+/string/ # regular expression
+<string # lexically lower
+>string # lexically greater
+=string # lexically equal
+
+(The absense of a "matcher" means value exists).
+
+
+Values
+------
+
+Any of the cond definitions, that extracts a value from the request
+$N 0 <= N <= 9, as grouped in a regular expression
+string (which can contain the above)
+null
+
+
+Examples
+--------
+
+cond %{HEADER:X-Y-Foobar}
+cond %{METHOD} =GET [OR]
+cond %{METHOD} =POST
+set-header X-Y-Fiefum %{HEADER:X-Y-Foobar}
+rm-header X-Y-Foobar
+rm-header Set-Cookie
+
+cond %{HEADER:X-Y-Foobar} "Some string" [AND,NC]
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/lulu.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/lulu.h b/plugins/experimental/header_rewrite/lulu.h
new file mode 100644
index 0000000..7452e49
--- /dev/null
+++ b/plugins/experimental/header_rewrite/lulu.h
@@ -0,0 +1,42 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __LULU_H__
+#define __LULU_H__ 1
+
+// Define UNUSED properly.
+#if ((__GNUC__ >= 3) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7)))
+#define UNUSED __attribute__ ((unused))
+#else
+#define UNUSED
+#endif /* #if ((__GNUC__ >= 3) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 7))) */
+
+static char UNUSED rcsId__lulu_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <sys/types.h>
+
+// Memory barriers on i386 / linux / gcc
+#if defined(__i386__)
+#define mb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define rmb() __asm__ __volatile__ ( "lock; addl $0,0(%%esp)" : : : "memory" )
+#define wmb() __asm__ __volatile__ ( "" : : : "memory")
+#elif defined(__x86_64__)
+#define mb() __asm__ __volatile__ ( "mfence" : : : "memory")
+#define rmb() __asm__ __volatile__ ( "lfence" : : : "memory")
+#define wmb() __asm__ __volatile__ ( "" : : : "memory")
+#else
+#error "Define barriers"
+#endif
+
+static const char* PLUGIN_NAME UNUSED = "header_rewrite";
+static const char* PLUGIN_NAME_DBG UNUSED = "header_rewrite_dbg";
+
+
+// From google styleguide: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+
+#endif // __LULU_H__
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/matcher.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/matcher.cc b/plugins/experimental/header_rewrite/matcher.cc
new file mode 100644
index 0000000..b520ab7
--- /dev/null
+++ b/plugins/experimental/header_rewrite/matcher.cc
@@ -0,0 +1,12 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// matcher.cc: Implementation of the condition classes
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__matcher_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "matcher.h"
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/matcher.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/matcher.h b/plugins/experimental/header_rewrite/matcher.h
new file mode 100644
index 0000000..3676a52
--- /dev/null
+++ b/plugins/experimental/header_rewrite/matcher.h
@@ -0,0 +1,159 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __MATCHER_H__
+#define __MATCHER_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__matcher_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+#include "regex_helper.h"
+#include <iostream> // For debugging
+
+#include "lulu.h"
+
+
+// Possible operators that we support (at least partially)
+enum MatcherOps {
+ MATCH_EQUAL,
+ MATCH_LESS_THEN,
+ MATCH_GREATER_THEN,
+ MATCH_REGULAR_EXPRESSION
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Matchers (this is also the interface)
+//
+class Matcher
+{
+public:
+ explicit Matcher(const MatcherOps op)
+ : _pdata(NULL), _op(op)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Matcher");
+ }
+
+ virtual ~Matcher() {
+ TSDebug(PLUGIN_NAME_DBG, "Calling DTOR for Matcher");
+ free_pdata();
+ }
+
+ void set_pdata(void* pdata) { _pdata = pdata; }
+ void* get_pdata() const { return _pdata; }
+ virtual void free_pdata() { TSfree(_pdata); _pdata = NULL; }
+
+protected:
+ void* _pdata;
+ const MatcherOps _op;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Matcher);
+};
+
+// Template class to match on various types of data
+template<class T>
+class Matchers : public Matcher
+{
+public:
+ explicit Matchers<T>(const MatcherOps op)
+ : Matcher(op)
+ { }
+
+ // Getters / setters
+ const T get() const { return _data; };
+
+ void setRegex(const std::string data)
+ {
+ if(!helper.setRegexMatch(_data))
+ {
+ std::cout<<"Invalid regex:failed to precompile"<<std::endl;
+ abort();
+ }
+ TSDebug(PLUGIN_NAME,"Regex precompiled successfully");
+
+ }
+
+ void setRegex(const unsigned int t)
+ {
+ return;
+ }
+
+ void setRegex(const TSHttpStatus t)
+ {
+ return;
+ }
+
+ void set (const T d)
+ {
+ _data = d;
+ if(_op == MATCH_REGULAR_EXPRESSION)
+ setRegex(d);
+ }
+
+ // Evaluate this matcher
+ bool
+ test(const T t) const {
+ switch (_op) {
+ case MATCH_EQUAL:
+ return test_eq(t);
+ break;
+ case MATCH_LESS_THEN:
+ return test_lt(t);
+ break;
+ case MATCH_GREATER_THEN:
+ return test_gt(t);
+ break;
+ case MATCH_REGULAR_EXPRESSION:
+ return test_reg(t);
+ break;
+ default:
+ // ToDo: error
+ break;
+ }
+ return false;
+ }
+
+private:
+ // For basic types
+ bool test_eq(const T t) const {
+ // std::cout << "Testing: " << t << " == " << _data << std::endl;
+ return t == _data;
+ }
+ bool test_lt(const T t) const {
+ // std::cout << "Testing: " << t << " < " << _data << std::endl;
+ return t < _data;
+ }
+ bool test_gt(const T t) const {
+ // std::cout << "Testing: " << t << " > " << _data << std::endl;
+ return t > _data;
+ }
+
+ bool test_reg(const unsigned int t) const {
+ // Not support
+ return false;
+ }
+
+ bool test_reg(const TSHttpStatus t) const {
+ // Not support
+ return false;
+ }
+
+ bool test_reg(const std::string t) const {
+ TSDebug(PLUGIN_NAME, "Test regular expression %s : %s", _data.c_str(), t.c_str());
+ int ovector[OVECCOUNT];
+ if (helper.regexMatch(t.c_str(), t.length(), ovector) > 0) {
+ TSDebug(PLUGIN_NAME, "Successfully found regular expression match");
+ return true;
+ }
+ return false;
+ }
+ T _data;
+ regexHelper helper;
+};
+
+#endif // __MATCHER_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operator.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operator.cc b/plugins/experimental/header_rewrite/operator.cc
new file mode 100644
index 0000000..d68cab3
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operator.cc
@@ -0,0 +1,32 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// operator.cc: Implementation of the operator base class
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operator_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+#include "operator.h"
+
+const OperModifiers
+Operator::get_oper_modifiers() const {
+ if (_next)
+ return static_cast<OperModifiers>(_mods | static_cast<Operator*>(_next)->get_oper_modifiers());
+
+ return _mods;
+}
+
+void
+Operator::initialize(Parser& p) {
+ Statement::initialize(p);
+
+ if (p.mod_exist("L")) {
+ _mods = static_cast<OperModifiers>(_mods | OPER_LAST);
+ }
+
+ if (p.mod_exist("QSA")) {
+ _mods = static_cast<OperModifiers>(_mods | OPER_QSA);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operator.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operator.h b/plugins/experimental/header_rewrite/operator.h
new file mode 100644
index 0000000..4323f15
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operator.h
@@ -0,0 +1,59 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for creating all operators. Don't user the operator.h interface
+// directly!
+//
+#ifndef __OPERATOR_H__
+#define __OPERATOR_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operator_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "statement.h"
+#include "parser.h"
+
+
+// Operator modifiers
+enum OperModifiers {
+ OPER_NONE = 0,
+ OPER_LAST = 1,
+ OPER_NEXT = 2,
+ OPER_QSA=4
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Operators (this is also the interface)
+//
+class Operator : public Statement
+{
+public:
+ Operator()
+ : _mods(OPER_NONE)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Operator");
+ }
+
+ void do_exec(const Resources& res) const {
+ exec(res);
+ if (NULL != _next)
+ static_cast<Operator*>(_next)->do_exec(res);
+ }
+
+ const OperModifiers get_oper_modifiers() const;
+
+ virtual void initialize(Parser& p);
+
+protected:
+ virtual void exec(const Resources& res) const = 0;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Operator);
+
+ OperModifiers _mods;
+};
+
+#endif // __OPERATOR_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operators.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operators.cc b/plugins/experimental/header_rewrite/operators.cc
new file mode 100644
index 0000000..c5db93a
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operators.cc
@@ -0,0 +1,366 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// operators.cc: implementation of the operator classes
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operators_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <arpa/inet.h>
+#include <ts/ts.h>
+#include <string.h>
+
+#include "operators.h"
+
+// OperatorRMHeader
+void
+OperatorRMHeader::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _header = p.get_arg();
+
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+ require_resources(RSRC_SERVER_REQUEST_HEADERS);
+ require_resources(RSRC_CLIENT_REQUEST_HEADERS);
+ require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorRMHeader::exec(const Resources& res) const
+{
+ TSMLoc field_loc, tmp;
+
+ if (res.bufp && res.hdr_loc) {
+ TSDebug(PLUGIN_NAME, "OperatorRMHeader::exec() invoked on header %s", _header.c_str());
+ field_loc = TSMimeHdrFieldFind(res.bufp, res.hdr_loc, _header.c_str(), _header.size());
+ while (field_loc) {
+ TSDebug(PLUGIN_NAME, "\tdeleting header %s", _header.c_str());
+ tmp = TSMimeHdrFieldNextDup(res.bufp, res.hdr_loc, field_loc);
+ TSMimeHdrFieldDestroy(res.bufp, res.hdr_loc, field_loc);
+ TSHandleMLocRelease(res.bufp, res.hdr_loc, field_loc);
+ field_loc = tmp;
+ }
+ }
+}
+
+
+// OperatorSetStatus
+void
+OperatorSetStatus::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _status.set_value(p.get_arg());
+
+ if (NULL == (_reason = TSHttpHdrReasonLookup((TSHttpStatus)_status.get_int_value()))) {
+ TSError("header_rewrite: unknown status %d", _status.get_int_value());
+ _reason_len = 0;
+ } else {
+ _reason_len = strlen(_reason);
+ }
+
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+ require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+ require_resources(RSRC_RESPONSE_STATUS);
+}
+
+
+void
+OperatorSetStatus::initialize_hooks() {
+ add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+ add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+}
+
+
+void
+OperatorSetStatus::exec(const Resources& res) const
+{
+ if (res.bufp && res.hdr_loc) {
+ TSHttpHdrStatusSet(res.bufp, res.hdr_loc, (TSHttpStatus)_status.get_int_value());
+ if (_reason && _reason_len > 0)
+ TSHttpHdrReasonSet(res.bufp, res.hdr_loc, _reason, _reason_len);
+ }
+}
+
+
+// OperatorSetStatusReason
+void
+OperatorSetStatusReason::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _reason.set_value(p.get_arg());
+ require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorSetStatusReason::initialize_hooks() {
+ add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+ add_allowed_hook(TS_HTTP_SEND_RESPONSE_HDR_HOOK);
+}
+
+void
+OperatorSetStatusReason::exec(const Resources& res) const {
+ if (res.bufp && res.hdr_loc) {
+ std::string reason;
+
+ _reason.append_value(reason, res);
+ if (reason.size() > 0) {
+ TSDebug(PLUGIN_NAME, "Setting Status Reason to %s", reason.c_str());
+ TSHttpHdrReasonSet(res.bufp, res.hdr_loc, reason.c_str(), reason.size());
+ }
+ }
+}
+
+
+// OperatorAddHeader
+void
+OperatorAddHeader::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _header = p.get_arg();
+ _value.set_value(p.get_value());
+
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+ require_resources(RSRC_SERVER_REQUEST_HEADERS);
+ require_resources(RSRC_CLIENT_REQUEST_HEADERS);
+ require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+}
+
+
+void
+OperatorAddHeader::exec(const Resources& res) const
+{
+// int IP = TSHttpTxnServerIPGet(res.txnp);
+// inet_ntop(AF_INET, &IP, buf, sizeof(buf));
+ std::string value;
+
+ _value.append_value(value, res);
+
+ // Never set an empty header (I don't think that ever makes sense?)
+ if (value.empty()) {
+ TSDebug(PLUGIN_NAME, "Would set header %s to an empty value, skipping", _header.c_str());
+ return;
+ }
+
+ if (res.bufp && res.hdr_loc) {
+ TSDebug(PLUGIN_NAME, "OperatorAddHeader::exec() invoked on header %s: %s", _header.c_str(), value.c_str());
+ TSMLoc field_loc;
+
+ if (TS_SUCCESS == TSMimeHdrFieldCreateNamed(res.bufp, res.hdr_loc, _header.c_str(), _header.size(), &field_loc)) {
+ if (TS_SUCCESS == TSMimeHdrFieldValueStringInsert(res.bufp, res.hdr_loc, field_loc, -1, value.c_str(), value.size())) {
+ TSDebug(PLUGIN_NAME, " adding header %s", _header.c_str());
+ //INKHttpHdrPrint(res.bufp, res.hdr_loc, reqBuff);
+ TSMimeHdrFieldAppend(res.bufp, res.hdr_loc, field_loc);
+ }
+ TSHandleMLocRelease(res.bufp, res.hdr_loc, field_loc);
+ }
+
+ }
+}
+
+
+/// TODO and XXX: These currently only support when running as remap plugin.
+// OperatorSetDestination
+void
+OperatorSetDestination::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _url_qual = parse_url_qualifier(p.get_arg());
+ _value.set_value(p.get_value());
+ // TODO: What resources would we require here?
+}
+
+
+void
+OperatorSetDestination::exec(const Resources& res) const
+{
+ if (res._rri) {
+ std::string value;
+
+ // Never set an empty destination value (I don't think that ever makes sense?)
+ switch (_url_qual) {
+
+ case URL_QUAL_HOST:
+ _value.append_value(value, res);
+ if (value.empty()) {
+ TSDebug(PLUGIN_NAME, "Would set destination HOST to an empty value, skipping");
+ } else {
+ const_cast<Resources&>(res).changed_url = true;
+ TSUrlHostSet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+ TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with HOST: %s", value.c_str());
+ }
+ break;
+
+ case URL_QUAL_PATH:
+ _value.append_value(value, res);
+ if (value.empty()) {
+ TSDebug(PLUGIN_NAME, "Would set destination PATH to an empty value, skipping");
+ } else {
+ const_cast<Resources&>(res).changed_url = true;
+ TSUrlHostSet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+ TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with PATH: %s", value.c_str());
+ }
+ break;
+
+ case URL_QUAL_QUERY:
+ _value.append_value(value, res);
+ if (value.empty()) {
+ TSDebug(PLUGIN_NAME, "Would set destination QUERY to an empty value, skipping");
+ } else {
+ //1.6.4--Support for preserving QSA in case of set-destination
+ if (get_oper_modifiers() & OPER_QSA) {
+ int query_len = 0;
+ const char* query = TSUrlHttpQueryGet(res._rri->requestBufp, res._rri->requestUrl, &query_len);
+ TSDebug(PLUGIN_NAME, "QSA mode, append original query string: %.*s", query_len, query);
+ //std::string connector = (value.find("?") == std::string::npos)? "?" : "&";
+ value.append("&");
+ value.append(query, query_len);
+ }
+
+ const_cast<Resources&>(res).changed_url = true;
+ TSUrlHttpQuerySet(res._rri->requestBufp, res._rri->requestUrl, value.c_str(), value.size());
+ TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with QUERY: %s", value.c_str());
+ }
+ break;
+
+ case URL_QUAL_PORT:
+ if (_value.get_int_value() <= 0) {
+ TSDebug(PLUGIN_NAME, "Would set destination PORT to an invalid range, skipping");
+ } else {
+ const_cast<Resources&>(res).changed_url = true;
+ TSUrlPortSet(res._rri->requestBufp, res._rri->requestUrl, _value.get_int_value());
+ TSDebug(PLUGIN_NAME, "OperatorSetHost::exec() invoked with PORT: %d", _value.get_int_value());
+ }
+ break;
+ case URL_QUAL_URL:
+ // TODO: Implement URL parser.
+ break;
+ default:
+ break;
+ }
+ } else {
+ // TODO: Handle the non-remap case here (InkAPI hooks)
+ }
+}
+
+
+/// TODO and XXX: These currently only support when running as remap plugin.
+// OperatorSetRedirect
+void
+OperatorSetRedirect::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ _status.set_value(p.get_arg());
+ _location.set_value(p.get_value());
+
+ if ((_status.get_int_value() != (int)TS_HTTP_STATUS_MOVED_PERMANENTLY) &&
+ (_status.get_int_value() != (int)TS_HTTP_STATUS_MOVED_TEMPORARILY)) {
+ TSError("header_rewrite: unsupported redirect status %d", _status.get_int_value());
+ }
+
+ require_resources(RSRC_SERVER_RESPONSE_HEADERS);
+ require_resources(RSRC_CLIENT_RESPONSE_HEADERS);
+ require_resources(RSRC_RESPONSE_STATUS);
+}
+
+
+void
+OperatorSetRedirect::exec(const Resources& res) const
+{
+ if (res._rri) {
+ if (res.bufp && res.hdr_loc) {
+ std::string value;
+
+ _location.append_value(value, res);
+
+ // Replace %{PATH} to original path
+ size_t pos_path = 0;
+
+ if ((pos_path = value.find("%{PATH}")) != std::string::npos) {
+ value.erase(pos_path, 7); // erase %{PATH} from the rewritten to url
+ int path_len = 0;
+ const char *path = TSUrlPathGet(res._rri->requestBufp, res._rri->requestUrl, &path_len);
+ if (path_len > 0) {
+ TSDebug(PLUGIN_NAME, "Find %%{PATH} in redirect url, replace it with: %.*s", path_len, path);
+ value.insert(pos_path, path, path_len);
+ }
+ }
+
+ // Append the original query string
+ int query_len = 0;
+ const char *query = TSUrlHttpQueryGet(res._rri->requestBufp, res._rri->requestUrl, &query_len);
+ if ((get_oper_modifiers() & OPER_QSA) && (query_len > 0)) {
+ TSDebug(PLUGIN_NAME, "QSA mode, append original query string: %.*s", query_len, query);
+ std::string connector = (value.find("?") == std::string::npos)? "?" : "&";
+ value.append(connector);
+ value.append(query, query_len);
+ }
+
+ TSHttpTxnSetHttpRetStatus(res.txnp,(TSHttpStatus)_status.get_int_value());
+ //TSHttpHdrStatusSet(res.bufp, res.hdr_loc, (TSHttpStatus)_status.get_int_value());
+ const char *start = value.c_str();
+ const char *end = value.size() + start;
+ TSUrlParse(res._rri->requestBufp, res._rri->requestUrl, &start, end);
+ TSDebug(PLUGIN_NAME, "OperatorSetRedirect::exec() invoked with destination=%s and status code=%d",
+ value.c_str(), _status.get_int_value());
+ }
+
+ } else {
+ // TODO: Handle the non-remap case here (InkAPI hooks)
+ }
+}
+
+
+// OperatorSetTimeoutOut
+void
+OperatorSetTimeoutOut::initialize(Parser& p) {
+ Operator::initialize(p);
+
+ if (p.get_arg() == "active") {
+ _type = TO_OUT_ACTIVE;
+ } else if (p.get_arg() == "inactive") {
+ _type = TO_OUT_INACTIVE;
+ } else if (p.get_arg() == "connect") {
+ _type = TO_OUT_CONNECT;
+ } else if (p.get_arg() == "dns") {
+ _type = TO_OUT_DNS;
+ } else {
+ _type = TO_OUT_UNDEFINED;
+ TSError("header_rewrite: unsupported timeout qualifier: %s", p.get_arg().c_str());
+ }
+
+ _timeout.set_value(p.get_value());
+}
+
+
+void
+OperatorSetTimeoutOut::exec(const Resources& res) const
+{
+ switch (_type) {
+ case TO_OUT_ACTIVE:
+ TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(active, %d)", _timeout.get_int_value());
+ TSHttpTxnActiveTimeoutSet(res.txnp, _timeout.get_int_value());
+ break;
+
+ case TO_OUT_INACTIVE:
+ TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(inactive, %d)", _timeout.get_int_value());
+ TSHttpTxnNoActivityTimeoutSet(res.txnp, _timeout.get_int_value());
+ break;
+
+ case TO_OUT_CONNECT:
+ TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(connect, %d)", _timeout.get_int_value());
+ TSHttpTxnConnectTimeoutSet(res.txnp, _timeout.get_int_value());
+ break;
+
+ case TO_OUT_DNS:
+ TSDebug(PLUGIN_NAME, "OperatorSetTimeoutOut::exec(dns, %d)", _timeout.get_int_value());
+ TSHttpTxnDNSTimeoutSet(res.txnp, _timeout.get_int_value());
+ break;
+ default:
+ TSError("header_rewrite: unsupported timeout");
+ break;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/operators.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/operators.h b/plugins/experimental/header_rewrite/operators.h
new file mode 100644
index 0000000..f85112c
--- /dev/null
+++ b/plugins/experimental/header_rewrite/operators.h
@@ -0,0 +1,192 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __OPERATORS_H__
+#define __OPERATORS_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__operators_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "operator.h"
+#include "resources.h"
+#include "value.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Operator declarations.
+//
+class OperatorRMHeader : public Operator
+{
+public:
+ OperatorRMHeader()
+ : _header("")
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorRMHeader");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorRMHeader);
+
+ std::string _header;
+};
+
+
+class OperatorSetStatus : public Operator
+{
+public:
+ OperatorSetStatus()
+ : _reason(NULL), _reason_len(0)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetStatus");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void initialize_hooks();
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorSetStatus);
+
+ Value _status;
+ const char* _reason;
+ int _reason_len;
+};
+
+
+class OperatorSetStatusReason : public Operator
+{
+public:
+ OperatorSetStatusReason()
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetStatusReason");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void initialize_hooks();
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorSetStatusReason);
+
+ Value _reason;
+};
+
+
+class OperatorAddHeader : public Operator
+{
+public:
+ OperatorAddHeader()
+ : _header("")
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorAddHeader");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorAddHeader);
+
+ std::string _header;
+ Value _value;
+};
+
+
+class OperatorSetDestination : public Operator
+{
+public:
+ OperatorSetDestination()
+ : _url_qual(URL_QUAL_NONE)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetDestination");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorSetDestination);
+
+ UrlQualifiers _url_qual;
+ Value _value;
+};
+
+
+class OperatorSetRedirect : public Operator
+{
+public:
+ OperatorSetRedirect()
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetRedirect");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorSetRedirect);
+
+ Value _status;
+ Value _location;
+};
+
+
+class OperatorNoOp : public Operator
+{
+public:
+ OperatorNoOp()
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorNoOp");
+ }
+
+protected:
+ void exec(const Resources& res) const { };
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorNoOp);
+};
+
+
+class OperatorSetTimeoutOut : public Operator
+{
+public:
+ OperatorSetTimeoutOut()
+ : _type(TO_OUT_UNDEFINED)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for OperatorSetTimeoutOut");
+ }
+ void initialize(Parser& p);
+
+protected:
+ void exec(const Resources& res) const;
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(OperatorSetTimeoutOut);
+
+ enum TimeoutOutType {
+ TO_OUT_UNDEFINED,
+ TO_OUT_ACTIVE,
+ TO_OUT_INACTIVE,
+ TO_OUT_CONNECT,
+ TO_OUT_DNS
+ };
+
+ TimeoutOutType _type;
+ Value _timeout;
+};
+
+
+#endif // __OPERATORS_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/parser.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/parser.cc b/plugins/experimental/header_rewrite/parser.cc
new file mode 100644
index 0000000..b0289d6
--- /dev/null
+++ b/plugins/experimental/header_rewrite/parser.cc
@@ -0,0 +1,107 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// parser.cc: implementation of the config parser
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__parser_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <utility>
+#include <boost/tokenizer.hpp>
+
+#include <ts/ts.h>
+
+#include "parser.h"
+
+// Tokenizer separators
+static boost::escaped_list_separator<char> token_sep('\\', ' ', '\"');
+static boost::char_separator<char> comma_sep(",");
+
+
+// This is the core "parser", parsing rule sets
+void
+Parser::preprocess(std::vector<std::string>& tokens)
+{
+ // Special case for "conditional" values
+ if (tokens[0].substr(0, 2) == "%{") {
+ _cond = true;
+ } else if (tokens[0] == "cond") {
+ _cond = true;
+ tokens.erase(tokens.begin());
+ }
+
+ // Is it a condition or operator?
+ if (_cond) {
+ if ((tokens[0].substr(0, 2) == "%{") && (tokens[0][tokens[0].size() - 1] == '}')) {
+ std::string s = tokens[0].substr(2, tokens[0].size() - 3);
+
+ _op = s;
+ if (tokens.size() > 1)
+ _arg = tokens[1];
+ else
+ _arg = "";
+ } else {
+ TSError("header_rewrite: conditions must be embraced in %%{}");
+ return;
+ }
+ } else {
+ // Operator has no qualifiers, but could take an optional second argumetn
+ _op = tokens[0];
+ if (tokens.size() > 1) {
+ _arg = tokens[1];
+ if (tokens.size() > 2)
+ _val = tokens[2];
+ else
+ _val = "";
+ } else {
+ _arg = "";
+ _val = "";
+ }
+ }
+
+ // The last token might be the "flags" section
+ if (tokens.size() > 0) {
+ std::string m = tokens[tokens.size() - 1];
+
+ if (!m.empty() && (m[0] == '[')) {
+ if (m[m.size() - 1] == ']') {
+ m = m.substr(1, m.size() - 2);
+ if (m.find_first_of(',') != std::string::npos) {
+ boost::tokenizer<boost::char_separator<char> > mods(m, comma_sep);
+ std::copy(mods.begin(), mods.end(), std::back_inserter(_mods));
+ } else {
+ _mods.push_back(m);
+ }
+ } else {
+ // Syntax error
+ TSError("header_rewrite: mods have to be embraced in []");
+ return;
+ }
+ }
+ }
+}
+
+
+Parser::Parser(const std::string& line) :
+ _cond(false), _empty(false)
+{
+ TSDebug("header_rewrite_dbg", "Calling CTOR for Parser");
+
+ if (line[0] == '#') {
+ _empty = true;
+ } else {
+ boost::tokenizer<boost::escaped_list_separator<char> > elem(line, token_sep);
+ std::vector<std::string> tokens;
+
+ for (boost::tokenizer<boost::escaped_list_separator<char> >::iterator it = elem.begin(); it != elem.end(); it++) {
+ // Skip "empty" tokens (tokenizer is a little brain dead IMO)
+ if ((*it) != "")
+ tokens.push_back(*it);
+ }
+
+ if (tokens.empty()) {
+ _empty = true;
+ } else {
+ preprocess(tokens);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/parser.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/parser.h b/plugins/experimental/header_rewrite/parser.h
new file mode 100644
index 0000000..0d0aa46
--- /dev/null
+++ b/plugins/experimental/header_rewrite/parser.h
@@ -0,0 +1,53 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Interface for the config line parser
+//
+#ifndef __PARSER_H__
+#define __PARSER_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__parser_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include "lulu.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+class Parser
+{
+public:
+ explicit Parser(const std::string& line);
+
+ bool empty() const { return _empty; }
+ bool is_cond() const { return _cond; }
+
+ bool cond_op_is(const std::string s) const { return _cond && (_op == s); }
+ bool oper_op_is(const std::string s) const { return !_cond && (_op == s); }
+
+ const std::string& get_op() const { return _op; }
+ std::string& get_arg() { return _arg; }
+ const std::string& get_value() const { return _val; }
+
+ bool mod_exist(const std::string m) const {
+ return (std::find(_mods.begin(), _mods.end(), m) != _mods.end());
+ }
+
+private:
+ void preprocess(std::vector<std::string>& tokens);
+ DISALLOW_COPY_AND_ASSIGN(Parser);
+
+ bool _cond;
+ bool _empty;
+ std::vector<std::string> _mods;
+ std::string _op;
+ std::string _arg;
+ std::string _val;
+};
+
+
+#endif // __PARSER_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/regex_helper.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/regex_helper.cc b/plugins/experimental/header_rewrite/regex_helper.cc
new file mode 100644
index 0000000..578253c
--- /dev/null
+++ b/plugins/experimental/header_rewrite/regex_helper.cc
@@ -0,0 +1,52 @@
+#include "regex_helper.h"
+
+bool
+regexHelper::setRegexMatch(const std::string& s)
+{
+
+ const char* errorComp = NULL;
+ const char* errorStudy = NULL;
+ int erroffset;
+
+ regexString = s;
+ regex = pcre_compile(regexString.c_str(), 0, &errorComp, &erroffset, NULL);
+
+ if (regex == NULL)
+ {
+ return false;
+ }
+ regexExtra = pcre_study(regex, 0, &errorStudy);
+ if ((regexExtra == NULL) && (errorStudy != 0))
+ {
+ return false;
+ }
+ if (pcre_fullinfo(regex, regexExtra, PCRE_INFO_CAPTURECOUNT, ®exCcount) != 0)
+ return false;
+ return true;
+ }
+
+const std::string&
+regexHelper::getRegexString() const
+{
+ return regexString;
+}
+
+int
+regexHelper::getRegexCcount() const
+{
+ return regexCcount;
+}
+
+int
+regexHelper::regexMatch(const char* str, int len, int ovector[]) const {
+ return pcre_exec(regex, // the compiled pattern
+ regexExtra, // Extra data from study (maybe)
+ str, // the subject std::string
+ len, // the length of the subject
+ 0, // start at offset 0 in the subject
+ 0, // default options
+ ovector, // output vector for substring information
+ OVECCOUNT); // number of elements in the output vector
+ };
+
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/regex_helper.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/regex_helper.h b/plugins/experimental/header_rewrite/regex_helper.h
new file mode 100644
index 0000000..6b43c31
--- /dev/null
+++ b/plugins/experimental/header_rewrite/regex_helper.h
@@ -0,0 +1,44 @@
+#ifndef REGEX_HELPER_H
+#define REGEX_HELPER_H
+
+#include <pcre.h>
+
+#include <string>
+
+
+const int OVECCOUNT = 30; // We support $1 - $9 only, and this needs to be 3x that
+
+class regexHelper{
+public:
+ regexHelper():
+ regex(NULL),regexExtra(NULL),regexCcount(0)
+ {
+
+ }
+
+ ~regexHelper()
+ {
+ if (regex)
+ pcre_free(regex);
+
+ if (regexExtra)
+ pcre_free(regexExtra);
+ }
+
+
+
+bool setRegexMatch(const std::string &s);
+const std::string& getRegexString() const;
+int getRegexCcount() const;
+int regexMatch(const char*,int,int ovector[]) const;
+
+private:
+ pcre* regex;
+ pcre_extra* regexExtra;
+ std::string regexString;
+ int regexCcount;
+
+};
+
+
+#endif
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/resources.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/resources.cc b/plugins/experimental/header_rewrite/resources.cc
new file mode 100644
index 0000000..1e1233e
--- /dev/null
+++ b/plugins/experimental/header_rewrite/resources.cc
@@ -0,0 +1,110 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// resources.cc: Implementation of the resources class.
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__resources_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "lulu.h"
+
+void
+Resources::gather(const ResourceIDs ids, TSHttpHookID hook)
+{
+ TSDebug(PLUGIN_NAME, "Building resource structure for hook (%d)", hook);
+
+ // If we need the client request headers, make sure it's also available in the client vars.
+ if (ids & RSRC_CLIENT_REQUEST_HEADERS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers");
+ if (TSHttpTxnClientReqGet(txnp, &client_bufp, &client_hdr_loc) != TS_SUCCESS) {
+ TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+ return;
+ }
+ }
+
+ switch (hook) {
+ case TS_HTTP_READ_RESPONSE_HDR_HOOK:
+ // Read response headers from server
+ if (ids & RSRC_SERVER_RESPONSE_HEADERS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN server response header buffers");
+ if (TSHttpTxnServerRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+ TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for response");
+ return;
+ }
+ }
+ if (ids & RSRC_RESPONSE_STATUS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN server response status resource");
+ resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+ }
+ break;
+
+ case TS_HTTP_SEND_REQUEST_HDR_HOOK:
+ // Read request headers to server
+ if (ids & RSRC_SERVER_REQUEST_HEADERS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN server request header buffers");
+ if (!TSHttpTxnServerReqGet(txnp, &bufp, &hdr_loc)) {
+ TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+ return;
+ }
+ }
+ break;
+
+ case TS_HTTP_READ_REQUEST_HDR_HOOK:
+ case TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK:
+ // Read request from client
+ if (ids & RSRC_CLIENT_REQUEST_HEADERS) {
+ bufp = client_bufp;
+ hdr_loc = client_hdr_loc;
+ }
+ break;
+
+ case TS_HTTP_SEND_RESPONSE_HDR_HOOK:
+ // Send response headers to client
+ if (ids & RSRC_CLIENT_RESPONSE_HEADERS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN client response header buffers");
+ if (TSHttpTxnClientRespGet(txnp, &bufp, &hdr_loc) != TS_SUCCESS) {
+ TSDebug(PLUGIN_NAME, "could not gather bufp/hdr_loc for request");
+ return;
+ }
+ if (ids & RSRC_RESPONSE_STATUS) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN client esponse status resource");
+ resp_status = TSHttpHdrStatusGet(bufp, hdr_loc);
+ }
+ }
+ break;
+
+ case TS_REMAP_PSEUDO_HOOK:
+ // Pseudo-hook for a remap instance
+ if (client_bufp && client_hdr_loc) {
+ TSDebug(PLUGIN_NAME, "\tAdding TXN client request header buffers for remap instance");
+ bufp = client_bufp;
+ hdr_loc = client_hdr_loc;
+ }
+ break;
+
+
+ default:
+ break;
+ }
+
+ _ready = true;
+}
+
+void
+Resources::destroy()
+{
+ if (bufp) {
+ if (hdr_loc)
+ TSHandleMLocRelease(bufp, TS_NULL_MLOC, hdr_loc);
+ }
+
+ if (client_bufp && (client_bufp != bufp)) {
+ if (client_hdr_loc && (client_hdr_loc != hdr_loc)) // TODO: Is this check really necessary?
+ TSHandleMLocRelease(client_bufp, TS_NULL_MLOC, client_hdr_loc);
+ }
+
+ _ready = false;
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/resources.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/resources.h b/plugins/experimental/header_rewrite/resources.h
new file mode 100644
index 0000000..db29170
--- /dev/null
+++ b/plugins/experimental/header_rewrite/resources.h
@@ -0,0 +1,75 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __RESOURCES_H__
+#define __RESOURCES_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__resources_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+#include <ts/remap.h>
+
+#include "lulu.h"
+
+
+#define TS_REMAP_PSEUDO_HOOK TS_HTTP_LAST_HOOK // Ugly, but use the "last hook" for remap instances.
+
+enum ResourceIDs {
+ RSRC_NONE = 0,
+ RSRC_SERVER_RESPONSE_HEADERS = 1,
+ RSRC_SERVER_REQUEST_HEADERS = 2,
+ RSRC_CLIENT_REQUEST_HEADERS = 4,
+ RSRC_CLIENT_RESPONSE_HEADERS = 8,
+ RSRC_RESPONSE_STATUS = 16,
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Resources holds the minimum resources required to process a request.
+//
+class Resources
+{
+public:
+ explicit Resources(TSHttpTxn txnptr, TSCont contptr)
+ : txnp(txnptr), contp(contptr), bufp(NULL), hdr_loc(NULL), client_bufp(NULL), client_hdr_loc(NULL),
+ resp_status(TS_HTTP_STATUS_NONE), _rri(NULL), changed_url(false), _ready(false)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Resources (InkAPI)");
+ }
+
+ Resources(TSHttpTxn txnptr, TSRemapRequestInfo *rri) :
+ txnp(txnptr), contp(NULL),
+ bufp(NULL), hdr_loc(NULL), client_bufp(NULL), client_hdr_loc(NULL), resp_status(TS_HTTP_STATUS_NONE),
+ _rri(rri), _ready(false)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Resources (RemapAPI)");
+ }
+
+ ~Resources() { destroy(); }
+
+ void gather(const ResourceIDs ids, TSHttpHookID hook);
+ bool ready() const { return _ready; }
+
+ TSHttpTxn txnp;
+ TSCont contp;
+ TSMBuffer bufp;
+ TSMLoc hdr_loc;
+ TSMBuffer client_bufp;
+ TSMLoc client_hdr_loc;
+ TSHttpStatus resp_status;
+ TSRemapRequestInfo *_rri;
+ bool changed_url;
+
+private:
+ void destroy();
+ DISALLOW_COPY_AND_ASSIGN(Resources);
+
+ bool _ready;
+};
+
+
+#endif // __RESOURCES_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/ruleset.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/ruleset.cc b/plugins/experimental/header_rewrite/ruleset.cc
new file mode 100644
index 0000000..6dce0fb
--- /dev/null
+++ b/plugins/experimental/header_rewrite/ruleset.cc
@@ -0,0 +1,75 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// ruleset.cc: implementation of the ruleset class
+//
+//
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__ruleset_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+
+#include "ruleset.h"
+#include "factory.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class implementation (no reason to have these inline)
+//
+void
+RuleSet::append(RuleSet* rule) {
+ RuleSet* tmp = this;
+
+ TSReleaseAssert(rule->next == NULL);
+
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = rule;
+}
+
+
+void
+RuleSet::add_condition(Parser& p) {
+ Condition* c = condition_factory(p.get_op());
+
+ if (NULL != c) {
+ TSDebug(PLUGIN_NAME, "Adding condition: %%{%s} with arg: %s\n", p.get_op().c_str(), p.get_arg().c_str());
+ c->initialize(p);
+ if (!c->set_hook(_hook)) {
+ TSError("header_rewrite: can't use this condition in this hook");
+ return;
+ }
+ if (NULL == _cond) {
+ _cond = c;
+ } else {
+ _cond->append(c);
+ }
+
+ // Update some ruleset state based on this new condition
+ _last |= c->last();
+ _ids = static_cast<ResourceIDs>(_ids | _cond->get_resource_ids());
+ }
+}
+
+
+void
+RuleSet::add_operator(Parser& p) {
+ Operator* o = operator_factory(p.get_op());
+
+ if (NULL != o) {
+ // TODO: This should be extended to show both the "argument" and the "value" (if both are used)
+ TSDebug(PLUGIN_NAME, "Adding operator: %s(%s)\n", p.get_op().c_str(), p.get_arg().c_str());
+ o->initialize(p);
+ if (!o->set_hook(_hook)) {
+ TSError("header_rewrite: can't use this operator in this hook");
+ return;
+ }
+ if (NULL == _oper) {
+ _oper = o;
+ } else {
+ _oper->append(o);
+ }
+
+ // Update some ruleset state based on this new operator
+ _opermods = static_cast<OperModifiers>(_opermods | _oper->get_oper_modifiers());
+ _ids = static_cast<ResourceIDs>(_ids | _oper->get_resource_ids());
+ }
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/ruleset.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/ruleset.h b/plugins/experimental/header_rewrite/ruleset.h
new file mode 100644
index 0000000..f0e13ad
--- /dev/null
+++ b/plugins/experimental/header_rewrite/ruleset.h
@@ -0,0 +1,81 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Implement the classes for the various types of hash keys we support.
+//
+#ifndef __RULESET_H__
+#define __RULESET_H__ 1
+
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__ruleset_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+
+#include "matcher.h"
+#include "factory.h"
+#include "resources.h"
+#include "parser.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class holding one ruleset. A ruleset is one (or more) pre-conditions, and
+// one (or more) operators.
+//
+class RuleSet
+{
+public:
+ RuleSet()
+ : next(NULL), _cond(NULL), _oper(NULL), _hook(TS_HTTP_READ_RESPONSE_HDR_HOOK), _ids(RSRC_NONE),
+ _opermods(OPER_NONE), _last(false)
+ { };
+
+ // No reason to inline these
+ void append(RuleSet* rule);
+
+ void add_condition(Parser& p);
+ void add_operator(Parser& p);
+ bool has_operator() const { return NULL != _oper; }
+ bool has_condition() const { return NULL != _cond; }
+
+ void set_hook(TSHttpHookID hook) { _hook = hook; }
+ const TSHttpHookID get_hook() const { return _hook; }
+
+ // Inline
+ const ResourceIDs get_all_resource_ids() const {
+ return _ids;
+ }
+
+ bool eval(const Resources& res) const {
+ if (NULL == _cond) {
+ return true;
+ } else {
+ return _cond->do_eval(res);
+ }
+ }
+
+ bool last() const {
+ return _last;
+ }
+
+ OperModifiers exec(const Resources& res) const {
+ _oper->do_exec(res);
+ return _opermods;
+ }
+
+ RuleSet* next; // Linked list
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(RuleSet);
+
+ Condition* _cond; // First pre-condition (linked list)
+ Operator* _oper; // First operator (linked list)
+ TSHttpHookID _hook; // Which hook is this rule for
+
+ // State values (updated when conds / operators are added)
+ ResourceIDs _ids;
+ OperModifiers _opermods;
+ bool _last;
+};
+
+
+#endif // __RULESET_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/statement.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/statement.cc b/plugins/experimental/header_rewrite/statement.cc
new file mode 100644
index 0000000..918c910
--- /dev/null
+++ b/plugins/experimental/header_rewrite/statement.cc
@@ -0,0 +1,84 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// statement.cc: Implementation of the statement base class.
+//
+//
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__statement_cc[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <ts/ts.h>
+
+#include "statement.h"
+
+void
+Statement::append(Statement* stmt)
+{
+ Statement* tmp = this;
+
+ TSReleaseAssert(stmt->_next == NULL);
+ while (tmp->_next)
+ tmp = tmp->_next;
+ tmp->_next = stmt;
+}
+
+
+const ResourceIDs
+Statement::get_resource_ids() const
+{
+ const Statement* stmt = this;
+ ResourceIDs ids = RSRC_NONE;
+
+ while (stmt) {
+ ids = static_cast<ResourceIDs>(ids | stmt->_rsrc);
+ stmt = stmt->_next;
+ }
+
+ return ids;
+}
+
+
+bool
+Statement::set_hook(TSHttpHookID hook) {
+ bool ret = std::find(_allowed_hooks.begin(), _allowed_hooks.end(), hook) != _allowed_hooks.end();
+
+ if (ret)
+ _hook = hook;
+
+ return ret;
+}
+
+
+// This should be overridden for any Statement which only supports some hooks
+void
+Statement::initialize_hooks() {
+ add_allowed_hook(TS_HTTP_READ_RESPONSE_HDR_HOOK);
+ add_allowed_hook(TS_HTTP_READ_REQUEST_PRE_REMAP_HOOK);
+ add_allowed_hook(TS_HTTP_READ_REQUEST_HDR_HOOK);
+ 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);
+}
+
+
+// Parse URL qualifiers
+UrlQualifiers
+Statement::parse_url_qualifier(const std::string& q) {
+ UrlQualifiers qual = URL_QUAL_NONE;
+
+ if (q == "HOST")
+ qual = URL_QUAL_HOST;
+ else if (q == "PORT")
+ qual = URL_QUAL_PORT;
+ else if (q == "PATH")
+ qual = URL_QUAL_PATH;
+ else if (q == "QUERY")
+ qual = URL_QUAL_QUERY;
+ else if (q == "MATRIX")
+ qual = URL_QUAL_MATRIX;
+ else if (q == "SCHEME")
+ qual = URL_QUAL_SCHEME;
+ else if (q == "URL")
+ qual = URL_QUAL_URL;
+
+ return qual;
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/statement.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/statement.h b/plugins/experimental/header_rewrite/statement.h
new file mode 100644
index 0000000..e42149b
--- /dev/null
+++ b/plugins/experimental/header_rewrite/statement.h
@@ -0,0 +1,92 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Base class for all Conditions and Operations. We share the "linked" list, and the
+// resource management / requirements.
+//
+#ifndef __STATEMENT_H__
+#define __STATEMENT_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__statement_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <vector>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "parser.h"
+#include "lulu.h"
+
+
+// URL data (both client and server)
+enum UrlQualifiers {
+ URL_QUAL_NONE,
+ URL_QUAL_HOST,
+ URL_QUAL_PORT,
+ URL_QUAL_PATH,
+ URL_QUAL_QUERY,
+ URL_QUAL_MATRIX,
+ URL_QUAL_SCHEME,
+ URL_QUAL_URL
+};
+
+
+class Statement
+{
+ public:
+ Statement()
+ : _next(NULL), _pdata(NULL), _rsrc(RSRC_NONE), _initialized(false), _hook(TS_HTTP_READ_RESPONSE_HDR_HOOK)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Statement");
+ }
+
+ virtual ~Statement() {
+ TSDebug(PLUGIN_NAME_DBG, "Calling DTOR for Statement");
+ free_pdata();
+ }
+
+ // Private data
+ void set_pdata(void* pdata) { _pdata = pdata; }
+ void* get_pdata() const { return(_pdata); }
+ virtual void free_pdata() { TSfree(_pdata); _pdata = NULL; }
+
+ // Which hook are we adding this statement to?
+ bool set_hook(TSHttpHookID hook);
+ const TSHttpHookID get_hook() const { return _hook; }
+
+ // Which hooks are this "statement" applicable for? Used during parsing only.
+ void add_allowed_hook(const TSHttpHookID hook) { _allowed_hooks.push_back(hook); }
+
+ // Linked list.
+ void append(Statement* stmt);
+
+ const ResourceIDs get_resource_ids() const;
+
+ virtual void initialize(Parser& p) {
+ TSReleaseAssert(_initialized == false);
+ initialize_hooks();
+ _initialized = true;
+ }
+ bool initialized() const { return _initialized; }
+
+ protected:
+ virtual void initialize_hooks();
+
+ UrlQualifiers parse_url_qualifier(const std::string& q);
+ void require_resources(const ResourceIDs ids) { _rsrc = static_cast<ResourceIDs>(_rsrc | ids); }
+
+ Statement* _next; // Linked list
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Statement);
+
+ void* _pdata;
+ ResourceIDs _rsrc;
+ bool _initialized;
+ std::vector<TSHttpHookID> _allowed_hooks;
+ TSHttpHookID _hook;
+};
+
+
+#endif // __STATEMENT_H
+
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/header_rewrite/value.h
----------------------------------------------------------------------
diff --git a/plugins/experimental/header_rewrite/value.h b/plugins/experimental/header_rewrite/value.h
new file mode 100644
index 0000000..81d7792
--- /dev/null
+++ b/plugins/experimental/header_rewrite/value.h
@@ -0,0 +1,74 @@
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Public interface for creating all values.
+//
+//
+#ifndef __VALUE_H__
+#define __VALUE_H__ 1
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__value_h[] = "@(#) $Id$ built on " __DATE__ " " __TIME__;
+
+#include <string>
+#include <ts/ts.h>
+
+#include "resources.h"
+#include "statement.h"
+#include "condition.h"
+#include "factory.h"
+#include "parser.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Base class for all Values (this is also the interface).
+//
+// TODO: This is very incomplete, we need to support linked lists of these,
+// which evaluate each component and create a "joined" final string.
+//
+class Value : Statement
+{
+public:
+ Value()
+ : _value(""), _int_value(-1), _cond_val(NULL)
+ {
+ TSDebug(PLUGIN_NAME_DBG, "Calling CTOR for Value");
+ };
+
+ void
+ set_value(const std::string& val)
+ {
+ _value = val;
+ if (_value.substr(0,2) == "%{") {
+ Parser parser(_value);
+
+ _cond_val = condition_factory(parser.get_op());
+ if (_cond_val) {
+ _cond_val->initialize(parser);
+ }
+ }
+ _int_value = strtol(_value.c_str(), NULL, 10);
+ }
+
+ void
+ append_value(std::string& s, const Resources& res) const {
+ if (_cond_val) {
+ _cond_val->append_value(s, res);
+ } else {
+ s += _value;
+ }
+ }
+
+ const std::string& get_value() const { return _value; }
+ int get_int_value() const { return _int_value; }
+
+ bool empty() const { return _value.empty(); }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(Value);
+
+ std::string _value;
+ int _int_value;
+ Condition* _cond_val;
+};
+
+
+#endif // __VALUE_H
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/README
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/README b/plugins/experimental/hipes/README
new file mode 100644
index 0000000..b12f722
--- /dev/null
+++ b/plugins/experimental/hipes/README
@@ -0,0 +1,25 @@
+This plugin implements the HIPES system.
+
+Plugin configuration
+--------------------
+
+ server:<host> Name of the server to send this request to
+ urlp:<name> Name of the query parameter for the service URL [url]
+ path:<path> Path to use for the service URL [/]
+ ssl Use SSL when connecting to the service [no]
+ service Service server, host[:port] [no default]
+ server Name of HIPES server, host[:port] [hipes.yimg.com]
+ active_timeout The active connection timeout in ms [no default]
+ no_activity_timeout The no activity timeout in ms [no default]
+ connect_timeout The connect timeout in ms [no default]
+ dns_timeout The DNS timeout [no default]
+
+
+The timeout options override the server defaults (from records.config),
+and only apply to the connection to the specific "service" host.
+
+RELEASES
+--------
+
+Version 1.0 (??/??/10)
+ - First release.
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/gen_escape.c
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/gen_escape.c b/plugins/experimental/hipes/gen_escape.c
new file mode 100644
index 0000000..e137372
--- /dev/null
+++ b/plugins/experimental/hipes/gen_escape.c
@@ -0,0 +1,49 @@
+/** @file
+
+ @section license License
+
+ 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 <stdio.h>
+#include <string.h>
+
+
+int main() {
+ unsigned char codes[32];
+ unsigned char hex;
+ int c;
+
+ memset(codes, 0, sizeof(codes));
+
+ for (c = 0; c <= 255; ++c) {
+ if ( ( (c >= '0') && (c <= '9') ) ||
+ ( (c >= 'A') && (c <= 'Z') ) ||
+ ( (c >= 'a') && (c <= 'z') ) ||
+ ( (c == '_') ) ||
+ ( (c == '-') ) ||
+ ( (c == '.') ) ) {
+ } else {
+ codes[c / 8] |= (1 << (7 - c % 8));
+ }
+ }
+
+ for (hex = 0; hex < 32; ++hex) {
+ printf("0x%02lX, ", codes[hex]);
+ if (!((hex+1) % 4))
+ printf("\n");
+ }
+}
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/hipes/hipes.cc
----------------------------------------------------------------------
diff --git a/plugins/experimental/hipes/hipes.cc b/plugins/experimental/hipes/hipes.cc
new file mode 100644
index 0000000..1bbdbee
--- /dev/null
+++ b/plugins/experimental/hipes/hipes.cc
@@ -0,0 +1,536 @@
+/** @file
+
+ @section license License
+
+ 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.
+*/
+
+#define UNUSED __attribute__ ((unused))
+static char UNUSED rcsId__url_remap_cc[] = "@(#) $Id: yfor_remap.cc 218 2009-04-25 01:29:16Z leifh $ built on " __DATE__ " " __TIME__;
+
+#include <stdio.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <string>
+
+#include <ts/remap.h>
+#include <ts/ts.h>
+
+static const char* PLUGIN_NAME = "hipes";
+static const char* HIPES_SERVER_NAME = "hipes.example.com";
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Escape a URL string.
+//
+int
+escapify_url(const char *src, int src_len, char* dst, int dst_len)
+{
+ // This bitmap is generated using the gen_escape.c prog.
+ static unsigned char codes_to_escape[32] = {
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xF9, 0x00, 0x3F,
+ 0x80, 0x00, 0x00, 0x1E,
+ 0x80, 0x00, 0x00, 0x1F,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF, 0xFF, 0xFF
+ };
+
+ static char hex_digit[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C',
+ 'D', 'E', 'F'
+ };
+
+ const char* from = src;
+ char* to = dst;
+ int len = 0;
+
+ // Sanity check
+ if (!src)
+ return -1;
+
+ while (from < (src + src_len)) {
+ register unsigned char c = *from;
+
+ if (len >= dst_len)
+ return -1; // Does not fit.... abort!
+
+ if (codes_to_escape[c / 8] & (1 << (7 - c % 8))) {
+ *to++ = '%';
+ *to++ = hex_digit[c / 16];
+ *to++ = hex_digit[c % 16];
+ len += 3;
+ } else {
+ *to++ = *from;
+ ++len;
+ }
+ ++from;
+ }
+ *to = '\0';
+
+ return len;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Unescape a string. Have to make sure the destination buffer is at least as
+// long as the source buffer.
+//
+char*
+unescapify(const char* src, char* dst, int len) {
+ const char* cur = src;
+ char* next;
+ char subStr[3];
+ int size;
+
+ subStr[2] = '\0';
+ while ((next = (char*)memchr(cur, '%', len))) {
+ size = next - cur;
+ if (size > 0) {
+ memcpy(dst, cur, size);
+ dst += size;
+ cur += size;
+ len -= size;
+ }
+
+ if (len > 2 && (*cur+1) != '\0' && *(cur+2) != '\0') {
+ subStr[0] = *(++cur);
+ subStr[1] = *(++cur);
+ len -= 2;
+ *dst = (char)strtol(subStr, (char**)NULL, 16);
+ } else {
+ *dst = *cur;
+ }
+ ++dst;
+ ++cur;
+ --len;
+ }
+
+ if (len > 0) {
+ memcpy(dst, cur, len);
+ dst += len;
+ }
+
+ return dst;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Class encapsulating one service configuration
+//
+struct HIPESService
+{
+ HIPESService()
+ : url_param("url"), path(""), svc_server(""), svc_port(80), ssl(false), hipes_server(HIPES_SERVER_NAME),
+ hipes_port(80), default_redirect_flag(1), x_hipes_header("X-HIPES-Redirect"),
+ active_timeout(-1), no_activity_timeout(-1), connect_timeout(-1), dns_timeout(-1)
+ { };
+
+ std::string url_param;
+ std::string path;
+ std::string svc_server;
+ int svc_port;
+ bool ssl;
+ std::string hipes_server;
+ int hipes_port;
+ unsigned int default_redirect_flag;
+ std::string x_hipes_header;
+ int active_timeout;
+ int no_activity_timeout;
+ int connect_timeout;
+ int dns_timeout;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Initialize the plugin.
+//
+int
+TSRemapInit(TSREMAP_INTERFACE *api_info, char *errbuf, int errbuf_size)
+{
+ if (!api_info) {
+ strncpy(errbuf, "[tsremap_init] - Invalid TSREMAP_INTERFACE argument", errbuf_size - 1);
+ return TS_ERROR;
+ }
+
+ if (api_info->tsremap_version < TSREMAP_VERSION) {
+ snprintf(errbuf, errbuf_size - 1, "[tsremap_init] - Incorrect API version %ld.%ld",
+ api_info->tsremap_version >> 16, (api_info->tsremap_version & 0xffff));
+ return TS_ERROR;
+ }
+
+ INKDebug("hipes", "plugin is succesfully initialized");
+ return TS_SUCCESS;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// One instance per remap.config invocation.
+//
+int
+tsremap_new_instance(int argc, char *argv[], ihandle *ih, char *errbuf, int errbuf_size)
+{
+ HIPESService* ri = new HIPESService;
+
+ *ih = static_cast<ihandle>(ri);
+
+ if (ri == NULL) {
+ INKError("Unable to create remap instance");
+ return -5;
+ }
+
+ for (int ix=2; ix < argc; ++ix) {
+ std::string arg = argv[ix];
+ std::string::size_type sep = arg.find_first_of(":");
+
+ if (sep == std::string::npos) {
+ INKError("Malformed options in url_remap: %s", argv[ix]);
+ } else {
+ std::string arg_val = arg.substr(sep + 1, std::string::npos);
+
+ if (arg.compare(0, 4, "urlp") == 0) {
+ ri->url_param = arg_val;
+ } else if (arg.compare(0, 4, "path") == 0) {
+ ri->path = arg_val;
+ if (arg_val[0] == '/')
+ ri->path = arg_val.substr(1);
+ else
+ ri->path = arg_val;
+ } else if (arg.compare(0, 3, "ssl") == 0) {
+ ri->ssl = true;
+ } else if (arg.compare(0, 7, "service") == 0) {
+ std::string::size_type port = arg_val.find_first_of(":");
+
+ if (port == std::string::npos) {
+ ri->svc_server = arg_val;
+ } else {
+ ri->svc_server = arg_val.substr(0, port);
+ ri->svc_port = atoi(arg_val.substr(port+1).c_str());
+ }
+ } else if (arg.compare(0, 6, "server") == 0) {
+ std::string::size_type port = arg_val.find_first_of(":");
+
+ if (port == std::string::npos) {
+ ri->hipes_server = arg_val;
+ } else {
+ ri->hipes_server = arg_val.substr(0, port);
+ ri->hipes_port = atoi(arg_val.substr(port+1).c_str());
+ }
+ } else if (arg.compare(0, 14, "active_timeout") == 0) {
+ ri->active_timeout = atoi(arg_val.c_str());
+ } else if (arg.compare(0, 19, "no_activity_timeout") == 0) {
+ ri->no_activity_timeout = atoi(arg_val.c_str());
+ } else if (arg.compare(0, 15, "connect_timeout") == 0) {
+ ri->connect_timeout = atoi(arg_val.c_str());
+ } else if (arg.compare(0, 11, "dns_timeout") == 0) {
+ ri->dns_timeout = atoi(arg_val.c_str());
+ } else {
+ INKError("Unknown url_remap option: %s", argv[ix]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+tsremap_delete_instance(ihandle ih)
+{
+ HIPESService* ri = static_cast<HIPESService*>(ih);
+
+ delete ri;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// This is the main "entry" point for the plugin, called for every request.
+//
+int
+tsremap_remap(ihandle ih, rhandle rh, REMAP_REQUEST_INFO *rri)
+{
+ const char* slash;
+ char* ptr;
+ HIPESService* h_conf = static_cast<HIPESService*>(ih);
+
+ if (NULL == h_conf) {
+ INKDebug("hipes", "Falling back to default URL on URL remap without rules");
+ return 0;
+ }
+
+ // Make sure we have a matrix parameter, anything without is a bogus request.
+ if (rri->request_matrix_size <= 0) {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+ return 0;
+ }
+
+ // Don't think this can/should happen, but safety first ...
+ if (rri->request_matrix_size > TSREMAP_RRI_MAX_PATH_SIZE) {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+ return 0;
+ }
+
+ // If there is a '/' in the matrix parameters, we know there are multiple service requests in
+ // the incoming URL, so nibble off the first one, and pass the rest as a HIPES URL to the service.
+ if ((slash = static_cast<const char*>(memchr(rri->request_matrix, '/', rri->request_matrix_size)))) {
+ char svc_url[TSREMAP_RRI_MAX_PATH_SIZE + 1];
+ char svc_url_esc[TSREMAP_RRI_MAX_PATH_SIZE + 1];
+ int len, query_len;
+
+ // Create the escaped URL, which gets passed over to the service as a url= param.
+ len = 8 + h_conf->hipes_server.size() + (rri->request_matrix_size - (slash - rri->request_matrix) - 1);
+ if (len > TSREMAP_RRI_MAX_PATH_SIZE) {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+ return 0;
+ }
+ snprintf(svc_url, TSREMAP_RRI_MAX_PATH_SIZE, "http://%s/%.*s", h_conf->hipes_server.c_str(), len, slash + 1);
+ INKDebug("hipes", "Service URL is %s", svc_url);
+
+ len = escapify_url(svc_url, len, svc_url_esc, TSREMAP_RRI_MAX_PATH_SIZE);
+ if (len < 0) {
+ return 0;
+ }
+ INKDebug("hipes", "Escaped service URL is %s(%d)", svc_url_esc, len);
+
+ // Prepare the new query arguments, make sure it fits
+ if (( (slash - rri->request_matrix) + 2 + h_conf->url_param.size() + len) > TSREMAP_RRI_MAX_PATH_SIZE) {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_REQUEST_URI_TOO_LONG);
+ return 0;
+ }
+
+ query_len = (slash - rri->request_matrix);
+ memcpy(rri->new_query, rri->request_matrix, query_len);
+ ptr = rri->new_query;
+ while ((ptr = static_cast<char*>(memchr(ptr, ';', (rri->new_query + query_len) - ptr))))
+ *ptr = '&';
+
+ rri->new_query[query_len++] = '&';
+ memcpy(rri->new_query + query_len, h_conf->url_param.c_str(), h_conf->url_param.size());
+ query_len += h_conf->url_param.size();
+ rri->new_query[query_len++] = '=';
+
+ memcpy(rri->new_query + query_len, svc_url_esc, len);
+ rri->new_query_size = query_len + len;
+ INKDebug("hipes", "New query is %.*s(%d)", rri->new_query_size, rri->new_query, rri->new_query_size);
+ } else {
+ // This is the "final" step in this HIPES URL, so don't point back to HIPES (or we'll never leave)
+ rri->new_query_size = rri->request_matrix_size;
+ memcpy(rri->new_query, rri->request_matrix, rri->request_matrix_size);
+ ptr = rri->new_query;
+ while ((ptr = static_cast<char*>(memchr(ptr, ';', (rri->new_query + rri->new_query_size) - ptr))))
+ *ptr = '&';
+
+ INKDebug("hipes", "New query is %.*s(%d)", rri->new_query_size, rri->new_query, rri->new_query_size);
+ }
+
+ // Test if we should redirect or not
+ bool do_redirect = false;
+ int redirect_flag = h_conf->default_redirect_flag;
+ char* pos = rri->new_query;
+
+ while (pos && (pos = (char*)memchr(pos, '_', rri->new_query_size - (pos - rri->new_query)))) {
+ if (pos) {
+ ++pos;
+ if ((rri->new_query_size - (pos - rri->new_query)) < 10) { // redirect=n
+ pos = NULL;
+ } else {
+ if ((*pos == 'r') && (!strncmp(pos, "redirect=", 9))) {
+ redirect_flag = *(pos + 9) - '0';
+ if ((redirect_flag < 0) || (redirect_flag > 2))
+ redirect_flag = h_conf->default_redirect_flag;
+ INKDebug("hipes", "Found _redirect flag in URL: %d\n", redirect_flag);
+ pos = NULL;
+ }
+ }
+ }
+ }
+
+ if (redirect_flag > 0) {
+ // Now check the incoming request header, and match up.
+ INKMBuffer bufp;
+ INKMLoc hdr_loc, field_loc;
+ bool has_error = false;
+
+ if (INKHttpTxnClientReqGet((INKHttpTxn)rh, &bufp, &hdr_loc)) {
+ field_loc = INKMimeHdrFieldFind(bufp, hdr_loc, h_conf->x_hipes_header.c_str(), h_conf->x_hipes_header.size());
+ if (field_loc) {
+ int hdr_flag;
+
+ if (INKMimeHdrFieldValueIntGet(bufp, hdr_loc, field_loc, 0, &hdr_flag) == INK_SUCCESS) {
+ // Alright, now match up this header flag with the request (or default) flag
+ INKDebug("hipes", "Extracted %s header with value %d", h_conf->x_hipes_header.c_str(), hdr_flag);
+ switch (redirect_flag) {
+ case 0:
+ if (hdr_flag == 2) {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+ has_error = true;
+ } // Everything else is a "no"
+ break;
+ case 1:
+ if (hdr_flag == 2) {
+ do_redirect = true;
+ } // Everything else is a "no"
+ break;
+ case 2:
+ if (hdr_flag == 2) {
+ do_redirect = true;
+ } else {
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+ has_error = true;
+ }
+ break;
+ default:
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_BAD_REQUEST);
+ has_error = true;
+ break;
+ }
+ }
+ INKHandleMLocRelease(bufp, hdr_loc, field_loc);
+ }
+ INKHandleMLocRelease(bufp, INK_NULL_MLOC, hdr_loc);
+ }
+ if (has_error)
+ return 1;
+ }
+
+ // If we redirect, just generate a 302 URL, otherwise update the RRI struct properly.
+ if (do_redirect) {
+ int len;
+
+ if (h_conf->ssl) {
+ // https://<host>:<port>/<path>?<query?\0
+ len = 5 + 3 + h_conf->svc_server.size() + 6 + 1 + h_conf->path.size() + 1 + rri->new_query_size + 1;
+ } else {
+ // http://<host>:<port>/<path>?<query?\0
+ len = 4 + 3 + h_conf->svc_server.size() + 6 + 1 + h_conf->path.size() + 1 + rri->new_query_size + 1;
+ }
+
+ if (len > TSREMAP_RRI_MAX_REDIRECT_URL) {
+ INKError("Redirect in HIPES URL too long");
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, (INKHttpStatus)414);
+ } else {
+ int port = -1;
+
+ pos = rri->redirect_url;
+
+ // HTTP vs HTTPS
+ if (h_conf->ssl) {
+ memcpy(pos, "https://", 8);
+ pos += 8;
+ if (h_conf->svc_port != 443)
+ port = h_conf->svc_port;
+ } else {
+ memcpy(pos, "http://", 7);
+ pos += 7;
+ if (h_conf->svc_port != 80)
+ port = h_conf->svc_port;
+ }
+
+ // Server
+ memcpy(pos, h_conf->svc_server.c_str(), h_conf->svc_server.size());
+ pos += h_conf->svc_server.size();
+
+ // Port
+ if (port != -1)
+ pos += snprintf(pos, 6, ":%d", port);
+
+ // Path
+ *(pos++) = '/';
+ if (h_conf->path.size() > 0) {
+ memcpy(pos, h_conf->path.c_str(), h_conf->path.size());
+ pos += h_conf->path.size();
+ }
+
+ // Query
+ if (rri->new_query_size > 0) {
+ *(pos++) = '?';
+ memcpy(pos, rri->new_query, rri->new_query_size);
+ pos += rri->new_query_size;
+ }
+
+ // NULL terminate the URL.
+ *pos = '\0';
+
+ rri->redirect_url_size = pos - rri->redirect_url + 1;
+ INKDebug("hipes", "Redirecting to %.*s", rri->redirect_url_size, rri->redirect_url);
+ *(rri->new_query) = '\0';
+ rri->new_query_size = 0;
+ INKHttpTxnSetHttpRetStatus((INKHttpTxn)rh, INK_HTTP_STATUS_MOVED_TEMPORARILY);
+ }
+ } else { // Not a redirect, so proceed normally
+ // Set timeouts (if requested)
+ if (h_conf->active_timeout > -1) {
+ INKDebug("hipes", "Setting active timeout to %d", h_conf->active_timeout);
+ INKHttpTxnActiveTimeoutSet((INKHttpTxn)rh, h_conf->active_timeout);
+ }
+ if (h_conf->no_activity_timeout > -1) {
+ INKDebug("hipes", "Setting no activity timeout to %d", h_conf->no_activity_timeout);
+ INKHttpTxnNoActivityTimeoutSet((INKHttpTxn)rh, h_conf->no_activity_timeout);
+ }
+ if (h_conf->connect_timeout > -1) {
+ INKDebug("hipes", "Setting connect timeout to %d", h_conf->connect_timeout);
+ INKHttpTxnConnectTimeoutSet((INKHttpTxn)rh, h_conf->connect_timeout);
+ }
+ if (h_conf->dns_timeout > -1) {
+ INKDebug("hipes", "Setting DNS timeout to %d", h_conf->dns_timeout);
+ INKHttpTxnDNSTimeoutSet((INKHttpTxn)rh, h_conf->dns_timeout);
+ }
+
+ // Set server ...
+ rri->new_host_size = h_conf->svc_server.size();
+ memcpy(rri->new_host, h_conf->svc_server.c_str(), rri->new_host_size);
+ INKDebug("hipes", "New server is %.*s", rri->new_host_size, rri->new_host);
+
+ // ... and port
+ rri->new_port = h_conf->svc_port;
+ INKDebug("hipes", "New port is %d", rri->new_port);
+
+ // Update the path
+ rri->new_path_size = h_conf->path.size();
+ memcpy(rri->new_path, h_conf->path.c_str(), rri->new_path_size);
+ INKDebug("hipes", "New path is %.*s", rri->new_path_size, rri->new_path);
+
+ // Enable SSL?
+ if (h_conf->ssl)
+ rri->require_ssl = 1;
+
+ // Clear previous matrix params
+ rri->new_matrix_size = -1;
+ }
+
+ // Step 3: Profit
+ return 1;
+}
+
+
+
+/*
+ local variables:
+ mode: C++
+ indent-tabs-mode: nil
+ c-basic-offset: 2
+ c-comment-only-line-offset: 0
+ c-file-offsets: ((statement-block-intro . +)
+ (label . 0)
+ (statement-cont . +)
+
+ end:
+
+ Indent with: /usr/bin/indent -ncs -nut -npcs -l 120 logstats.cc
+*/
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/AUTHORS
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/AUTHORS b/plugins/experimental/memcached_remap/AUTHORS
new file mode 100644
index 0000000..945968f
--- /dev/null
+++ b/plugins/experimental/memcached_remap/AUTHORS
@@ -0,0 +1,7 @@
+Author: opensource@navyaprabha.com
+
+memcached_remap plugin
+
+========= CREDITS ===============
+Eric Balsa <er...@apache.org>
+Original concept via mysql_remap plugin
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/Changelog
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/Changelog b/plugins/experimental/memcached_remap/Changelog
new file mode 100644
index 0000000..3d16c80
--- /dev/null
+++ b/plugins/experimental/memcached_remap/Changelog
@@ -0,0 +1,2 @@
+1.0.0 20-May-2011
+ * Initial Release for 2.1.8-unstable apache release
http://git-wip-us.apache.org/repos/asf/trafficserver/blob/bc13c5e0/plugins/experimental/memcached_remap/NOTICE
----------------------------------------------------------------------
diff --git a/plugins/experimental/memcached_remap/NOTICE b/plugins/experimental/memcached_remap/NOTICE
new file mode 100644
index 0000000..c19f064
--- /dev/null
+++ b/plugins/experimental/memcached_remap/NOTICE
@@ -0,0 +1,5 @@
+Apache Traffic Server
+Copyright 2010 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).