20 May 2012, 16:02

Migrating to PSGI/Plack

Share

Have you heard of PSGI/Plack?

It’s that awesome Perl meta-webframework you’re looking for. Unfortuneately it’s not that easy to get started with because the documentation is a bit too euphoric. They talk about “superglue” and “duct tape for the web” but fall a bit short of explaining how to get started with it.

Shortcut: It you’re using one of the supported frameworks (like Catalyst oder CGI::Application 4.5+) you shouldn’t need to work about anything of this. How to get those running w/ PSGI is explained in sufficient details in various docs.

This post is for those who need to do it w/o an framework or want to know the internals. Well, I’m not going into great detail in this post, only the essentials.

  • What is PSGI? PSGI is a specification of a protocol spoken between a PSGI compliant Server and the WebApp. The WebApp is talking PSGI to it’s executing server and this server is talking whatever he likes to his downstream (e.g. HTTP, FCGI, …)
  • What is Plack? Plack is a set of tools implementing PSGI. It provides some PSGI compliant Server implementations on its own and help others building their PSGI servers. It’s also some kind of meta-framework (“middleware”) that implements such things like Session management, authentication and compression. You don’t have to use those features! You don’t even need to known that they are there, but if you want you can have a look.
  • What is $env? This is a HashRef containing the environment for your request? Why env, you ask? Well, probably because CGI get it’s parameters passed via the OS ‘environment’ and they just adopted that for PSGI.
  • What is the PSGI-triplet? Well, that’s just the name I gave to the data structure PSGI expects to get returned after a request has been processed. It contains the HTTP-Statuscode, an ArrayRef with the HTTP-Header pairs and an ArrayRef containing the Body.
So, how do I migrate my framework-less perl web application to being PSGI compliatnt?
  • Throw out CGI, CGI::Carp and possibly FCGI. You won’t be using them anymore. You’ll be using Plack::Request instead.
  • Make your App “persistence-compatible”. That means you’ll have to abandon any global (class) variables thats only valid for one request. Every class variable must be valid throughout the entire runtime of your serverĀ  (because your class is instantiated only once, at the PSGI-Servers startup. There are excpetions to this, but keep it as a rule of thumb). Every information that is only valid for one request must be passed between the methods. If you have much pass around put if into a custom request class or a HashRef.
  • Make sure your class has a method handling the request. You probably have that already, but you should name it ‘run’. It will get passed the $env. Use that directly or create your custom per-request data structure from there.
  • Remove any direct output to STDOUT. Make your app return the PSGI-triplet to the caller. Everywhere.
  • Create a webapp.psgi, see below for it’s content and possible a webapp.pl if you want to support plain old CGI.
  • plackup webapp.psgi
webapp.psgi

#!/usr/bin/perl
use strict;
use warnings;

use lib '../lib';

use MyApp::Web::Frontend;

# The important part here is to instantiate your WebApp Class before
# the closure is defined. Everything inside the sub is executed on each
# request. If you instantiate your class inside you'll loose any benefit
# you get by not using CGI.
my $Frontend = MyApp::Web::Frontend::->new();
my $app = sub {
    my $env = shift;

    return $Frontend->run($env);
};

webapp.pl

#!/usr/bin/perl
use strict;
use warnings;

# Warning: This script will only work properly when invoked with
# the correct environment. Plack::Loader tries to autodetect the
# proper server and will use CGI only if certain CGI environment
# variables are set. It will most specifically not work properly
# when run from the commandline.
use Plack::Loader;

my $app = Plack::Util::load_psgi("webapp.psgi");
Plack::Loader::->auto->run($app);