Mounting Apps with Plack

Plack has the ability to map applications onto URLs. This is facilitated with Plack::App::URLMap. Here I will demonstrate how one can use Plack and URLMap to mount multiple applications. In addition, we examine how to handle static file requests and requests to “/”.

Table of Contents

Load the application library paths

I have three Catalyst applications and one Web::Simple application that I’d like to load. Here I make sure their modules are in @INC.

use strictures 1;
use lib '/home/user/apps/mojomojo/lib';
use lib '/home/user/apps/Bracket/lib';
use lib '/home/user/apps/HomePage/lib';
use lib '/home/user/apps/Mojito/lib';

Load some Plack modules

Plack::Builder provides us with a Domain Specific Language (DSL) to easily manage multi-app URL mounting while Plack::Util allows us to load a Plack application from a file. Further, Plack::App::File is used to load static files (e.g. /css/*). Finally, Plack::App::Cascade is used to allow two applications to handle requests to “/*”.

use Plack::Builder;
use Plack::Util;
use Plack::App::File;
use Plack::App::Cascade;

Create PSGI apps from Catalyst apps

Three of the applications we want to load and mount are Catalyst based. The approach is to get a PSGI application that we’ll point a URL at. Here’s how we get a PSGI Catalyst application:

use MojoMojo;
MojoMojo->setup_engine('PSGI');
my $mojomojo_app = sub { MojoMojo->run(@_) };

use Bracket;
Bracket->setup_engine('PSGI');
my $bracket_app = sub { Bracket->run(@_) };

use HomePage;
HomePage->setup_engine('PSGI');
my $homepage_app = sub { HomePage->run(@_) };

Load a PSGI app from a file

I have a fourth application that I would like to mount. I use load_psgi in this example to load a Web::Simple psgi application from a file1.

my $mojito_app = Plack::Util::load_psgi '/home/user/apps/Mojito/app/mojito.pl';

Using Plack::App::File and Cascade

I’d like to be able be able to serve static files as well as a “root” page for the domain. Here I use Plack::App::File to create a static file server application, and I embed a very simple app which will handle the requests to “/”. Both apps are then cascaded together into $cascaded_root_app with $static_app getting the first shot at responding followed by $root_app

my $static_app = Plack::App::File->new(root => "/home/user/www");
my $root_app = sub { [200, ['Content-type''text/html'],['Hola els meus amics.']] };
my $cascaded_root_app = Plack::App::Cascade->new(apps => [$static_app$root_app ])->to_app;

Mapping apps to URLs

OK so we have a few PSGI apps now that we can mount. Here’s how it’s done:

builder {
    mount "/wiki"    => builder {
        enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }
          "Plack::Middleware::ReverseProxy";
        $mojomojo_app;
    };
    mount "/bracket" => builder {
        enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' }
          "Plack::Middleware::ReverseProxy";
        $bracket_app;
    };
    mount "/homie"   => $homepage_app;
    mount "/note"    => $mojito_app;
    mount "/"        => $cascaded_root_app;
};

So we’ve mounted 4 applications to four different /${mount_points} and we’ve setup the root of the server to handle static requests. In addition, a request for “/” will be handled by the $root_app we have cascaded with the $static_app.

Furthermore, one may notice the use of Plack::Middleware::ReverseProxy for a couple of the Catalyst apps. This allows us to proxy to these applications from something like nginx and have valid internal URLs.

Nginx Deployment

So now that we have a PSGI multi-mounted application, let’s call it my_web_apps.psgi we can start it up with plackup like so:

plackup -s Starman my_web_apps.psgi

This will start and mount on the applications on the default port of 5000 using the Starman backend. If I’d like to have an nginx front-end proxy to this multi-app I can configure /etc/nginx/sites-enabled/default something like:

upstream webapps_backend_url {
    server 127.0.0.1:5000;
}

...

server {
...
  location / {
    proxy_pass       http://webapps_backend_url;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header Host $http_host;
  }
...
}

Closing

In summary, one can see the flexibility of the Plack family of modules to mount multiple applications and even cascade them. Once a person has a multi-app psgi file they can choose whatever backend server they like to run it against, Starman in this example. In addition, nginx makes it easy to reverse-proxy to the applications with a few configuration lines. Lastly one could control the start/stop of the applications with an init.d script. See jshirley’s example for starters.

Foot Notes

1 The use of load_psgi could be employed with the Catalyst apps as well.

References

My tags:
 
Popular tags:
 
Powered by MojoMojo