Perlbal::Manual::Plugins - Creating and using plugins
Perlbal 1.78.
How to create and use Perlbal plugins.
Perlbal supports plugins through modules under Perlbal::Plugin::* that implement a set of functions described further down this document.
Perlbal::Plugin::*
Some of these plugins are shipped with Perlbal itself, while others can be found on CPAN (you can also create your own plugin and have it available only locally).
In order to use a plugin you first have to load it; on your Perlbal's configuration file add something like:
Load MyPlugin
This loads plugin Perlbal::Plugin::MyPlugin.
Perlbal::Plugin::MyPlugin
Each plugin will have its own way of being configured (some don't require any configuration at all), so you'll have to refer to their documentation (or code).
Typically (but not always), a plugin will allow you to set additional parameters to a service; for instance:
LOAD MaxContentLength CREATE SERVICE example SET max_content_length = 100000 SET plugins = MaxContentLength
max_content_length is a parameter of Perlbal::Plugin::MaxContentLength.
max_content_length
If you're worried that two plugins may have the same parameter, of if you simply want to define those variables all in the same spot and thus will be doing it outside of the plugin's context, you can use the more verbose syntax:
SET my_service.my_plugin.my_variable = my_value
Notice that some plugins need to be stated service by service; hence, this line:
SET plugins = MaxContentLength
The plugins parameter (a list of strings separated by commas or spaces) defines which plugins are acceptable for a service.
plugins
If you try to load a plugin and receive the following error message:
ERROR: Can't locate Perlbal/Plugin/MyPlugin.pm in @INC
That means that either the plugin isn't installed or perlbal couldn't find it. (perhaps it is installed in a different version of perl other than the one used to run perlbal?)
A Perlbal plugin consists in a package under the Perlbal::Plugin namespace that implements a number of functions: register, unregister, load and unload.
Perlbal::Plugin
register
unregister
load
unload
These steps and functions (plus some helper functions you can define or use) are described below.
PLEASE KEEP IN MIND: Perlbal is a single-process, asynchronous web server. You must not do things in plugins which will cause it to block, or no other requests can be served at the same time.
While there are many ways of creating a package, we'd recommend that you use something to do it for you. A good option is Module::Starter.
(note: if you really want to, you can just create a file with your package and use it; by using something like Module::Starter you're making sure that several pitfalls are avoided, lots of basic rules are followed and that your package can easily be made available as a distribution that you can deploy on any machine - or, if you feel so inclined, upload to CPAN - in a simple way)
Let's assume you want to create a plugin that checks requests for a X-Magic header and, if present, add an header X-Color to the response when serving a file. Let's assume your plugin will be called Perlbal::Plugin::ColorOfMagic.
X-Magic
X-Color
Perlbal::Plugin::ColorOfMagic
Having installed Module::Starter, here's a command you can run that will create your package for you:
$ module-starter --module=Perlbal::Plugin::ColorOfMagic --author="My name" --email=my@email.address
That should create a file tree that you can get better acquainted with by reading Module::Starter's fine documentation. For this example, the file you really need should now reside in lib/Perlbal/Plugin/ColorOfMagic.pm.
lib/Perlbal/Plugin/ColorOfMagic.pm
This file probably starts with something like the following:
package Perlbal::Plugin::ColorOfMagic; use warnings; use strict;
You'll have to add a few functions to this file. These are described below.
(note: upon creating this package, some boilerplate documentation will also be present on the file; you should revise it and even remove bits that don't feel right for your plugin)
register is called when the plugin is being added to a service. This is where you register your plugin's hooks, if required (see Perlbal::Manual::Hooks for the list of existing hooks and further documentation on how they work).
For the sake of our example (Perlbal::Plugin::ColorOfMagic, see above), what we want to do is register a hook that modifies the response headers; that means we want a modify_response_headers hook.
modify_response_headers
Here's what you'd do:
sub register { my ($class, $service) = @_; my $my_hook_code = sub { my Perlbal::ClientHTTPBase $cp = shift; if ( $cp->{req_headers}->header('X-Magic') ) { $cp->{res_headers}->header( 'X-Color', 'Octarine' ); } return 0; }; $service->register_hook('ColorOfMagic','modify_response_headers', $my_hook_code); }
Inside register, we're calling register_hook to register our ColorOfMagic modify_response_headers hook. Its code, that will run "when we've set all the headers, and are about to serve a file" (see Perlbal::Manual::Hooks), receives a Perlbal::ClientHTTPBase object (you can see what kind of object your hook will receive on Perlbal::Manual::Hooks). We're checking to see if X-Magic is defined on the request and, if so, we're setting header X-Color on the response to Octarine.
register_hook
ColorOfMagic
Octarine
Notice that the hook ends with return 0. This is because returning a true value means that you want to cancel the connection to the backend and send the response to the client yourself.
return 0
unregister is called when the plugin is removed from a service. It's a standard good practice to unregister your plugin's hooks here, like so:
sub unregister { my ($class, $service) = @_; $service->unregister_hooks('ColorOfMagic'); return 1; }
You can also use unregister_hook to unregister one single hook:
unregister_hook
$service->unregister_hook('ColorOfMagic', 'modify_response_headers');
load is called when your plugin is loaded (or reloaded).
This is where you should perform your plugin's initialization, which can go from setting up some variables to registering a management command (to register commands see the documentation for manage_command further down this document).
manage_command
my $color; sub load { my $class = shift; $color = 'Octarine'; return 1; }
load must always be defined, but if you really don't need it you can have it simply returning a true value:
sub load { return 1; }
unload is called when your plugin is unloaded. This is where you should perform any clean up tasks.
unload must always be defined, but if you really don't need it you can have it simply returning a true value:
sub unload { return 1; }
Don't forget to call unregister_global_hook if you have registered any (see the documentation for manage_command further down this document and you'll see what we're talking about).
unregister_global_hook
load is called when the plugin is loaded, while register is called whenever the plugin is set for a service.
This means that you should use load for anything that is global, such as registering a global hook, and you should use register for things that are specific to a service, such as registering service hooks.
dumpconfig is not required.
dumpconfig
When managing Perlbal (see Perlbal::Manual::Management) you can send a dumpconfig command that will result in a configuration dump.
Apart from the global configuration, each plugin that implements a dumpconfig function will also have that function called.
dumpconfig should return an array of messages to be displayed.
sub dumpconfig { my ($class, $service) = @_; my @messages; push @messages, "COLOROFMAGIC is $color"; return @messages; }
Again, dumpconfig is not required, so implement it only if it makes sense for your plugin.
Adding a tunable will allow you to set its value within each plugin:
LOAD MyPlugin CREATE SERVICE my_service SET my_new_parameter = 42 SET plugins = MyPlugin ENABLE my_service
add_tunable can be used by plugins that want to add tunables so that the config file can have more options for service settings.
add_tunable
sub load { Perlbal::Service::add_tunable( my_new_parameter => { check_role => '*', check_type => 'int', des => "description of my new parameter", default => 0, }, ); return 1; }
check_role defines for which roles the value can be set (reverse_proxy, web_server, etc). A value of * mean that the value can be set for any role.
check_role
reverse_proxy
web_server
*
The acceptable values for check_type are enum, regexp, bool, int, size, file, file_or_none and directory_or_none. An Unknown check_type error message will be displayed whenever you try to set a value that has an unknown check_type.
check_type
enum
regexp
bool
int
size
file
file_or_none
directory_or_none
check_type can also contain a code reference that will be used to validate the type.
check_type => sub { my $self = shift; my $val = shift; my $emesg = shift; ... },
This code reference should return a true or false value. If returning false, the contents of $emesg (which is passed as a reference to the function) will be used as the error message.
$emesg
Here's a better explanation of the acceptable values for check_type:
Boolean value. Must be defined and will be checked as a Perl value.
The value needs to be defined and the content must be an existing directory (validated against perl's -d switch).
An array reference containing the acceptable values:
check_type => [enum => ["yellow", "blue", "green"]],
A filename, validated against perl's -f switch.
A filename, validated against perl's -f switch, or the default value.
An integer value, validated against /^\d+$/.
/^\d+$/
Regular expression.
The correct form of setting a regexp tunable is by setting it as an array reference containing the type (regexp), the regular expression and a message that can explain it:
check_type => ["regexp", qr/^\d+\.\d+\.\d+\.\d+:\d+$/, "Expecting IP:port of form a.b.c.d:port."],
A size, validated against /^(\d+)[bkm]$/.
/^(\d+)[bkm]$/
Perlbal catches unknown configuration commands and tries to match them against hooks in the form of manage_command.*.
manage_command.*
Let's say that you want to set a management command time that will allow you to see what time it is on the server.
time
sub load { Perlbal::register_global_hook('manage_command.time', sub { my $time = localtime time; return [ "It is now:", $time ]; }); return 1; }
If you want to display a text message you should return an array reference; each of the values will be printed with a trailing newline character:
time It is now: Wed Dec 1 19:08:58 2010
If you need to parse additional parameters on your hook, you can use parse and args on the Perlbal::ManageCommand object that your function will receive:
parse
args
my $mc = shift; $mc->parse(qr/^time\s+(today|tomorrow)$/, "usage: TIME [today|tomorrow]"); my ($cmd, $choice) = $mc->args;
This would allow you to call your command with an argument that would have to be one of today or tomorrow.
today
tomorrow
register_setter allows you to define parameters that can be set for your plugin, using a syntax such as:
register_setter
For instance:
SET discworld.colorofmagic.color = 'Orange'
Here's how you'd configure a new setter, by using register_setter inside load:
my $color; sub load { $color = 'Octarine'; $svc->register_setter('ColorOfMagic', 'color', sub { my ($out, $what, $val) = @_; return 0 unless $what && $val; $color = $val; $out->("OK") if $out; return 1; }); return 1; }
For plugins that will work with a selector service, sometimes you'll want to override the selector itself.
selector
You can do this in register:
sub register { my ($class, $svc) = @_; $svc->selector(\&my_selector_function);
Don't forget to unregister your function on the way out:
sub unregister { my ($class, $svc) = @_; $svc->selector(undef); return 1; }
Your selector function receives a Perlbal::ClientHTTPBase object.
my Perlbal::ClientHTTPBase $cb = shift;
Inside your selector function you can set which service to forward the request to like this:
my $service = Perlbal->service($service_name); $service->adopt_base_client($cb); return 1;
See Perlbal::Plugin::Vhosts or Perlbal::Plugin::Vpaths for examples on how to do this.
The following is a list of known plugins:
Perlbal::Plugin::AccessControl (Perlbal core)
Basic access control based on IPs and Netmasks.
Perlbal::Plugin::Addheader
Add Headers to Perlbal webserver responses.
Perlbal::Plugin::AutoRemoveLeadingDir (Perlbal core)
Auto-removal of leading directory path components in the URL.
Perlbal::Plugin::BackendHeaders
See which backend served the request.
Perlbal::Plugin::Cgilike (Perlbal core)
Handle Perlbal requests with a Perl subroutine.
Perlbal::Plugin::EchoService (Perlbal core)
Simple plugin demonstrating how to create an add-on service for Perlbal using the plugin infrastructure.
Perlbal::Plugin::ExpandSSL
Add a custom header according to the SSL of a service.
Perlbal::Plugin::FlvStreaming (Perlbal core)
Enable FLV streaming with reverse proxy.
Perlbal::Plugin::ForwardedFor
Rename the X-Forwarded-For header in Perlbal.
Perlbal::Plugin::Highpri (Perlbal core)
Makes some requests high priority.
Perlbal::Plugin::Include (Perlbal core)
Allows multiple, nesting configuration files.
Perlbal::Plugin::LazyCDN (Perlbal core)
Support for Content Delivery Networks.
Perlbal::Plugin::MaxContentLength (Perlbal core)
Reject large requests.
Perlbal::Plugin::NotModified (Perlbal core)
Automatic 304 Not Modified responses when clients send a If-Modified-Since header.
If-Modified-Since
Perlbal::Plugin::PSGI
PSGI web server on Perlbal.
Perlbal::Plugin::Palimg (Perlbal core)
Plugin that allows Perlbal to serve palette altered images.
Perlbal::Plugin::Queues (Perlbal core)
Simple queue length header inclusion plugin.
Perlbal::Plugin::Redirect (Perlbal core)
Plugin to do redirecting in Perlbal land.
Perlbal::Plugin::Stats (Perlbal core)
Basic Perlbal statistics gatherer.
Perlbal::Plugin::StickySessions
Session affinity for Perlbal.
Perlbal::Plugin::Throttle (Perlbal core)
Throttle connections from hosts that connect too frequently.
Perlbal::Plugin::TrustHeader
Remove untrusted headers.
Perlbal::Plugin::UrlGroup
Let URL match it in regular expression.
Perlbal::Plugin::Vhosts (Perlbal core)
Name-based virtual hosts.
Perlbal::Plugin::Vpaths (Perlbal core)
Select by path (selector role only).
Perlbal::Plugin::XFFExtras (Perlbal core)
Perlbal plugin that can optionally add an X-Forwarded-Port and/or X-Forwarded-Proto header to reverse proxied requests.
Perlbal::Manual::Hooks, Perlbal::Manual::Internals.
There are sample configuration files under conf/; some of these are examples on how to use and configure existing plugins: echoservice.conf for Perlbal::Plugin::EchoService, virtual-hosts.conf for Perlbal::Plugin::VHosts, etc.
To install Perlbal, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Perlbal
CPAN shell
perl -MCPAN -e shell install Perlbal
For more information on module installation, please visit the detailed CPAN module installation guide.