package Toadfarm;
=head1 NAME
Toadfarm - One Mojolicious app to rule them all
=head1 VERSION
0.0801
=head1 SYNOPSIS
=head2 Production
You can start the application by running:
$ toadfarm myconfig.conf;
C<myconfig.conf> should contain a list with the application you want to run
and a set of HTTP headers to act on. Example:
{
apps => [
'My::App' => {
'X-Request-Base' => 'http://mydomain.com/whatever',
},
'/path/to/my-app' => {
'Host' => 'mydomain.com',
},
],
}
The config above will run C<My::App> when the "X-Request-Base" header is set
Or it will pass the request on to C</path/to/my-app> if the "Host" header is
set to "mydomain.com".
The apps are processed in the order they are defined. This means that the
first app that match will be executed.
=head2 Debug
It is possible to start the server in foreground as well:
$ MOJO_CONFIG=myconfig.conf toadfarm prefork
$ MOJO_CONFIG=myconfig.conf toadfarm daemon
See other options by running:
$ toadfarm -h
=head1 DESCRIPTION
This application can be used to load other L<Mojolicious> apps inside one app.
This could be useful if you want to save memory or instances on dotcloud or
heroku.
=head1 CONFIG FILE
Additional config params.
{
apps => [...], # See SYNOPSIS
log => {
file => '/path/to/log/file.log',
level => 'debug', # debug, info, warn, ...
combined => 1, # true to make all applications log to the same file
},
hypnotoad => {
listen => ['http://*:1234'],
workers => 12,
# ...
},
paths => {
renderer => [ '/my/custom/template/path' ],
static => [ '/my/custom/static/path' ],
},
plugins => [
MojoPlugin => CONFIG,
],
}
=over 4
=item * log
Used to set up where L<Toadfarm> should log to. It is also possible to set
"combined" to true if you want all the other apps to log to the same file.
=item * hypnotoad
See L<Mojo::Server::Hypnotoad/SETTINGS> for more "hypnotoad" settings.
=item * paths
Set this to enable custom templates and public files for this application.
This is useful if you want your own error templates or serve other assets from
L<Toadfarm>.
=item * plugins
"plugins" can be used to load plugins into L<Toadfarm>. The plugins are loaded
after the "apps" are loaded. They will receive the C<CONFIG> as the third
argument:
sub register {
my($self, $app, CONFIG) = @_;
# ...
}
See also: L<Toadfarm::Plugin::Reload/SYNOPSIS>.
=back
=head1 EXAMPLE SETUP
resources which show how to start L<Toadfarm> on ubuntu. In addition, you can
forward all traffic to the server using the "iptables" rule below:
$ iptables -A PREROUTING -i eth0 -p tcp -m tcp --dport 80 -j REDIRECT --to-ports 8080
=head1 PLUGINS
L<Toadfarm::Plugin::Reload>.
=cut
use Mojo::Util 'class_to_path';
our $VERSION = '0.0801';
=head1 METHODS
=head2 startup
This method will read the C<MOJO_CONFIG> and mount the applications specified.
=cut
sub startup {
my $self = shift;
my $config = $ENV{MOJO_CONFIG} ? $self->plugin('Config') : {};
if($config->{log}{file}) {
my $log = Mojo::Log->new;
$log->path($config->{log}{file});
$log->level($config->{log}{level} || 'info');
$self->log($log);
}
for my $type (qw/ renderer static /) {
my $paths = $config->{paths}{$type} or next;
$self->$type->paths($paths);
}
$self->_start_apps(@{ $config->{apps} }) if $config->{apps};
$self->_start_plugins(@{ $config->{plugins} }) if $config->{plugins};
# need to add the root app afterwards
if(my $app = delete $self->{root_app}) {
$self->log->info("Mounting $app->[0] without condition");
$self->routes->route('/')->detour(app => $app->[1]);
}
}
sub _start_apps {
my $self = shift;
my $routes = $self->routes;
my $n = 0;
if($self->config->{log}{combined}) {
$self->log->info('All apps will log to ' .$self->config->{log}{file});
}
while(@_) {
my($path, $rules) = (shift @_, shift @_);
my($app, $request_base, @over);
delete local $ENV{MOJO_CONFIG};
$path = class_to_path $path unless -e $path;
$app = Mojo::Server->new->load_app($path);
$app->log($self->log) if $self->config->{log}{combined};
while(my($name, $value) = each %$rules) {
$request_base = $value if $name eq 'X-Request-Base';
push @over, "return 0 unless +(\$h->header('$name') // '') eq '$value';\n";
}
if(@over) {
$app->log->info("Mounting $path with conditions");
unshift @over, "sub { my \$h = \$_[1]->req->headers;\n";
push @over, "\$_[1]->req->url->base(Mojo::URL->new('$request_base'));" if $request_base;
push @over, "return 1; }";
$routes->add_condition("toadfarm_condition_$n", => eval "@over" || die "@over: $@");
$routes->route('/')->detour(app => $app)->over("toadfarm_condition_$n");
}
else {
$self->{root_app} = [ $path => $app ];
}
$n++;
}
$self;
}
sub _start_plugins {
my $self = shift;
unshift @{ $self->plugins->namespaces }, 'Toadfarm::Plugin';
while(@_) {
my($plugin, $config) = (shift @_, shift @_);
$self->log->info("Loading plugin $plugin");
$self->plugin($plugin, $config);
}
}
=head1 AUTHOR
Jan Henning Thorsen - C<jhthorsen@cpan.org>
=cut
1;