Architecture

Extending

TxnBox is intended to be easily extendible with regard to adding new directives, comparisons, and extractors. For each such extension there are two major phases that must be supported, loading and invoking.

Loading is parsing data in the configuration file. Invoking happens during transaction processing.

Extractor

An extractor gathers data and provides it to the plugin. While this is usually data in a transaction that is not required. The gathered data is called a feature. Every extractor must be able to provide its feature in string format. It can also provide the feature in one of a few predefined feature types -

  • INTEGER, a signed integral value.

  • BOOL, a boolean value that is the equivalent of true and false.

  • IP_ADDR, an IP address.

Other feature types may be supported in the future.

An extractor must inherit from Extractor.

Comparison

A comparison compares fixed data to a feature and potentially provides some side effects on a successful match. At a minimum, the comparison class implementation must provide a function operator overload which performs the run time comparison with the feature. This can will be one or more of several overloads. The precise methods depend on which feature types are supported.

  • String - feature argument type is swoc::TextView.

  • Boolean - feature argument type is bool.

  • Number - feature argument type is intmax_t.

  • IP Address - feature argument type is swoc::IPAddr.

This is the only required elements, but there are several others which are very convienient and should be added unless there is a reason to not do so.

  • A static const std::string which contains the name of the comparison.

  • A static const FeatureMask which contains the valid feature types for the comparison.

  • A static load method which constructs an instance from YAML configuration.

  • Storage for the comparison fixed data.

The former could be hardwired wherever it is used, but having a single copy is better. The load method could be done with a free function or a lambda but again it is better to consolidate this in the class itself.

To illustrate, consider the suffix comparison. The bare minimum class definition is:

class Cmp_Suffix : public Comparison {
public:
   bool operator() (Context& ctx, TextView& text) const override;
};

Because Cmp_Suffix class is only valid for string features, it provides only the TextView overload. The more complete declaration would be:

class Cmp_Suffix : public Comparison {
public:
   static const std::string KEY;
   static const FeatureMask TYPES;

   bool operator() (Context& ctx, TextView& text) const override;

   static Rv<Handle> load(Config& cfg, YAML::Node cmp_node, YAML::Node key_node);

protected:
   Extractor::Expr _value; ///< Suffix value to compare.
};

The is_valid_for method is defined to return true only for the VIEW feature type, which is consistent with providing only the swoc::TextView function operator overload. That method calls TextView::ends_with to do the check.

In order to be available to the configuration, the comparison must be passed, along with its name, to the Comparison::define method. There are various initialization mechanism, the one used inside TxnBox looks like this:

namespace {
[[maybe_unused]] bool INITIALIZED = [] () -> bool {
   Comparison::define(Cmp_Suffix::KEY, Cmp_Suffix::TYPES, &Cmp_Suffix::load);

   return true;
} ();

More detail is available at Cmp_Suffix.