———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
Look at L<https://github.com/jhthorsen/toadfarm/tree/etc/> for example
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
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;