Perl Programming/HTTP::Engine

=HTTP::Engine=

HTTP::Engine is made by Kazuhiro Osawa (yappo). It is an generalized interface for application developers that makes it easy to deploy under different kinds of server configurations, including CGI, FastCGI, mod_perl and standalone pure-Perl HTTP Server. It is not, however, a full-stack Web application framework, but it'll be easy to write a framework based on it.

One application core with multiple interfaces
For example, this is a little application just outputs "Hello World": The run method here takes an instance of HTTP::Engine::Request as its input, and returns an HTTP::Engine::Response object as its output. This sub-routine is the first-handler from HTTP server. In a more complex application, it usually just dispatches the real work to some other sub-routine based on the URI or parameters.

To deploy it as a CGI script, you then write an "hello.cgi" like this:

use MyApp; use HTTP::Engine;
 * 1) !/usr/bin/perl

HTTP::Engine->new(   interface => {        module => 'CGI',        request_handler => sub {            MyApp->run(@_);        }    } )->run;

If someday, you need to deploy this application under mod_perl environment, you can just write a <tt>MyApp::ModPerl</tt> handler class with code like this:

package MyApp::ModPerl; use Any::Moose; extends 'HTTP::Engine::Interface::ModPerl';

use MyApp; use HTTP::Engine;

sub create_engine { my($class, $r, $context_key) = @_;

HTTP::Engine->new(       interface => {            module => "ModPerl",            request_handler => sub {                MyApp->run(@_);            }        }    ); }

__PACKAGE__->meta->make_immutable; no Any::Moose; 1;

Your core application code does not need to be modified, so long as you only use <tt>HTTP::Engine::*</tt> classes to construct your response.

Testing
You might question that why bother having an abstraction layer instead of just choose stick to a good deploy environment. One good answer is that might not be easy to test the app without this abstraction layer.

If your deployment configuration of choice is like <tt>mod_perl</tt>, then your test programs would need to start up an apache httpd with <tt>mod_perl.so</tt> loaded, and then try to hit it with maybe <tt>Test::WWW::Mechanize</tt>. That would work, too, but it's too troublesome.

<tt>HTTP::Engine</tt> comes with a specialized Test interface that makes this very easy. To test if our "Hello World" application works, you write a test program like this:

use strict; use warnings;
 * 1) test.pl

use Test::More tests => 1;

use MyApp; use HTTP::Engine; use HTTP::Request; use HTTP::Response;

my $engine = HTTP::Engine->new(   interface => {        module => 'Test',        request_handler => sub {            return MyApp->run(@_);        }    } );

my $response = $engine->run(   HTTP::Request->new(GET => 'http://localhost/'),    env => \%ENV,    connection_info => {        request_uri => "/"    } );

is($response->content, "Hello World", "The application does output Hello World!");

If you compared the construction statement of <tt>$engine</tt> object, you will see the only difference is the "module" value. This "Test" module makes the <tt>$engine</tt> object a totally mocked-up environment that basically just prepares every environment variables needed, and just execute your application code.

This makes it very lightweight and easy to run and write the test program. You can then write functional tests just like that way you write unit tests.