Plack::App::Hostname - Run multiple apps dispatched by the request Host header
version 1.001
Plack::App::Hostname->new ->map_hosts_to( $redirector_app => glob '{www.,}example.{com,net,org}' ) ->map_hosts_to( $content_app => 'example.org' ) ->map_hosts_to( $admin_app => 'edit.example.org' ) ->map_hosts_to( $tenant_app => '**.example.org' ) ->to_app;
This PSGI application dispatches requests to any number of other applications based on the Host request header. This is sometimes referred to as virtual hosting. The mapping as well as the configuration is reconfigurable at runtime with immediate effect. It is fast in the simple case but will accept a custom match callback to support complex scenarios.
Host
One likely "complex" scenario is deployment on a multiprocess forking server, where updates to the mapping only take effect per worker process. Arranging for all workers to update their mappings in lockstep is fiddly but a custom matcher doing lookups against some type of IPC-able table is not.
Instances of this application can be introspected to ask whether a particular hostname can be dispatched. This makes it easy to make other parts of compound PSGI applications adapt to changes in the dynamic mapping.
These are the named arguments you can pass to new. All of these are also accessors if you prefer to write them that way.
new
An app that will be called if nothing else matched. You might want to use this for some kind of default page.
If you do not provide this, a 400 error page will be returned for such requests.
An app that will be called in case the client did not provide a Host header at all. This should only occur with legacy clients that do not speak HTTP/1.1.
A code ref that will be invoked if the hostname was not found in the internal mapping. You can use this to implement your own dynamic mapping.
The hostname will be passed in $_ and will already be lowercased. The return value should be either a PSGI application code reference or undef.
$_
undef
E.g.
Plack::App:Hostname->new( custom_matcher => sub { $dbix_simple ->query( 'SELECT COUNT(*) FROM site WHERE hostname = ?', $_ ) ->into( my $is_known ); return $is_known ? $customer_site_app : undef; } );
Or even:
Plack::App:Hostname->new( custom_matcher => sub { return unless /^(\d+)\.wat\.info$/; my $n = $1; sub { [ 200, ['Content-Type','text/html'], ['w'.('a' x $n).'t'] ] } } );
You can go hog wild here.
$hostmap->map_hosts_to( $app => 'example.com', 'www.example.com' );
Maps any number of hostnames to an application. Remaps any existing mapping for these hostnames.
You can include a ** wildcard prefix to dispatch that entire (sub)domain to an app, e.g. **.example.org will match foo.example.org bar.example.org foo.bar.example.org foo.bar.baz.example.org etc. Note that this wildcard will not match example.org itself.
**
**.example.org
foo.example.org
bar.example.org
foo.bar.example.org
foo.bar.baz.example.org
example.org
The order in which hostnames are added is irrelevant: matches are always tried in order of decreasing specificity. So a matching non-wildcard hostname always takes precedence, and otherwise the longest matching pattern wins.
$hostmap->unmap_app( $admin_app );
Unmaps the given application from all hostnames it is mapped to.
You can pass several applications to unmap at once.
$hostmap->unmap_host( 'www.example.net' );
Unmaps the given hostname.
Wildcards have no special meaning. If you request unmapping **.example.org, only the single entry for **.example.org will be unmapped (if any).
You can pass several hostnames to unmap at once.
my $app = $hostmap->to_app;
Returns the application code reference for the Plack::App::Hostname instance.
$hostmap->matching( 'www.example.com' );
Returns the application mapped to a known hostname.
It first tries to find an application in its internal mapping, then calls your custom_matcher if one is configured. If both attempts fail, it returns undef.
custom_matcher
Note that this will not fall back to your default_app. This makes it usable as a predicate to test whether the Plack::App::Hostname instance in question will serve a given hostname. (It's easy enough to do the fallback explicitly if needed.)
default_app
Wildcard matching for a specific number of levels, i.e. *.*.example.com would match foo.bar.example.com but neither www.example.com nor foo.bar.baz.example.com. But an implementation of this must not have undue runtime cost.
*.*.example.com
foo.bar.example.com
www.example.com
foo.bar.baz.example.com
Patches welcome.
Performs a much more complex job: it dispatches not only based on hostname, but also based on the path, modifying the PATH_INFO accordingly. It is much more convenient if you need that, but also much slower to dispatch, and only provides a static mapping that cannot be modified after initialisation.
PATH_INFO
This supports a *. prefix to match all subdomains of a domain, but requires Domain::PublicSuffix for this feature, and includes caching to reduce the overhead incurred by that. The mapping is theoretically modifiable at runtime, but the caching complicates matters: there is no interface to clear it, so you either have to turn it off or monkey with internals.
*.
Mappings are specified as a series of patterns to try in order. This is slower than a hash lookup in the simple case, but due to lack of dynamic matching, not flexible enough for complex cases either. I consider this a bad trade-off: you pay extra and gain little; mostly, you just pay extra.
Aristotle Pagaltzis <pagaltzis@gmx.de>
This software is copyright (c) 2016 by Aristotle Pagaltzis.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
To install Plack::App::Hostname, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Plack::App::Hostname
CPAN shell
perl -MCPAN -e shell install Plack::App::Hostname
For more information on module installation, please visit the detailed CPAN module installation guide.