Tatsuhiko Miyagawa's Blog

Dynamically enabling Plack middleware

November 17, 2009

When you use middleware components in your PSGI app with Plack, you often want to enable them conditionally based on runtime environments. 

For example, some administrative middleware like debug-toolbar or stacktrace should be enabled if it’s accessed from your office network, and js/css-minifying middleware and image-resizing transcoder should be enabled if the requesting client is the mobile device or from known-to-be-slow network, etc.

I’ve looked at WSGI and Rack middleware and couldn’t see a generic way of handling this: most middleware components have their own configuration options, for instance the rack-bug has ip_masks options which checks the request’s address to activate the behavior not. I don’t like to see individual middleware component implement this kind of options since that’d be very inconsistent and confusing. 

So I thought that it’s easy to make a wrapping middleware that conditionally runs the wrapped middleware or passes through, based on the runtime environments.

So there you go: Plack::Middleware::Conditional and enable_if DSL sugar. Here are some examples from the docs:

# Minify JavaScript if the browser is Firefox
enable_if { $_[0]->{HTTP_USER_AGENT} =~ /Firefox/ } 'JavaScriptMinifier';

# Enable Stacktrace when being accessed from the local network
enable_if { $_[0]->{REMOTE_ADDR} =~ /^10\.0\.1\.*/ } 'StackTrace';

# Work with other conditional setter middleware:
# Transcode Jpeg on the fly for mobile clients
builder {
  enable 'MobileDetector';
  enable_if { $_[0]->{'psgix.mobile_detected'} }
    'TranscodeJpeg', max_size => 30_000;
  $app;
};

The nice thing about it is that now those conditional routine can be implemented as a piece of middleware like scook’s mobiledetector and can be used in other middleware’s enable_if clause and that’s really nice. Note that MobileDetector should come first because it’s a pre-run condition check which runs from outer to inner, which is from the top to the bottom with Builder DSL.

You can of course still use enable … if … to enable middleware statically in the server boot time rather than runtime. enable_if dynamic builder is like adding a hole in the onion layers.

This is now implemented as Plack 0.9.12 and is available on CPAN via cpanf :)