Reading code - plackup Architecture
- Part 1 - An Overview
- Part 2 - plackup Architecture
- Part 3 - PSGI Application Architecture
- Part 4 - Plack::Builder
Plack::Runner and plackup
plackup
starts a PSGI server which executes a PSGI application. However
the script itself is just a very small wrapper around Plack::Runner which does
all the heavy lifting. Plack::Runner
- parses the command line options.
- instantiates the chosen loader class (which is in the Plack::Loader namespace).
- instatiates the chosen server library (which is in the Plack::Handler namespace).
- starts the PSGI server and passes it a PSGI application
Plack::Loader
Loaders are responsible for instantiating and running the PSGI server. Here are
the more interesting capabilities a $loader
object has:
$loader->guess()
guesses which server library should be loaded by looking at command line opts, $ENV, and %INC.$loader->load()
instantiates the server library and returns the object.$loader->run()
starts the server.
The Plack::Loader namespace contains 3 kinds of loaders:
- Plack::Loader::Delayed - delays compilation of the web app until the first request occurs
- Plack::Loader::Restarter - reloads the server if any files are changed
- Plack::Loader::Shotgun - foreach request, forks a child which compiles the web app and runs it
I can choose which loader I want using plack --loader
Plack::Handler
The PSGI spec tells me that PSGI defines the interface between an application and a server. Because the PSGI spec is (intentionally) very minimal, there is a good deal of wiggle room to interpret how an application and a server might want to play together.
A library in the Plack::Handler namespace is the place where the application meets the server. This layer contains all the wiggling.
Lets say I wrote a new server called AngryBrontosaurus and I want to be able to
use it with plackup --server AngryBrontosaurus
. I could implement a small
class like this:
package Plack::Handler::AngryBrontosaurus
use strict;
use AngryBrontosaurus;
sub new {
my $class = shift;
bless { @_ }, $class;
}
sub run {
my ($self, $app) = @_;
AngryBrontosaurus->new->run($app, $self);
}
Then, to make sure AngryBrontosaurus and Plack::Handler::AngryBrontosaurus correctly implement the PSGI spec, I should also test my code with Plack::Test::Suite.
use Test::More;
use Plack::Test::Suite;
Plack::Test::Suite->run_server_tests('AngryBrontosaurus');
done_testing;
Notice that while the Plack::Handler namespace contains classes for several PSGI servers like Plack::Handler::Starman or Plack::Handler::Twiggy, it also includes some classes like Plack::Handler::Apache2 and Plack::Handler::FCGI. Clearly Apache2 was not written with PSGI compliance in mind, but there is glue in the Plack::Handler::Apache2 layer to enable it to speak with PSGI compliant applications.
Sequence diagram
This diagram describes how Plack::Runner, Plack::Handler, and Plack::Loader interact.