Intro to HTTP::Server::Simple::CGI
Last week I needed to create a persistent Perl service to listening for incoming files and then process them. For the protocol I went with HTTP, because it is more than adequate to meet my humble needs. So all I needed was (A) an implementation of an HTTP server in Perl, and (B) a clue how to use it.
Solving the first half was pretty easy, and I quickly fell upon the HTTP::Server::Simple. From there I realized that it would be easiest to write my service using its CGI specific subclass HTTP::Server::Simple::CGI.
Installation was typically painless:
sudo perl -MCPAN -e shell
install HTTP::Server::Simple::CGI
And then I was ever so slightly stuck. There is documentation on how to create a simple CGI server, but it wasn't quite enough to get me to the service I needed.
The skeleton of creating a server looks like this:
#!/usr/local/bin/perl -w
package MyServer;
use strict;
use HTTP::Server::Simple::CGI;
use base qw(HTTP::Server::Simple::CGI);
my $port = 4569;
sub handle_request {
my ($self, $cgi) = @_;
return "Hi.";
}
my $pid = MyServer->new(4116)->background();
This is quite simple and concise, but then again our server isn't actually doing anything useful yet. That's where things get a bit less obviously documented, especially if you're unfamiliar with the CGI module, which is they key that unlocks all mysteries.
Here are a few recipes for writing the handle_request
subprocess.
Vary Response On HTTP Method
sub handle_request {
my ($self, $cgi) = @_;
my $method = $ENV{REQUEST_METHOD};
my $timestamp = time();
if (lc($method) eq 'post') {
print "Received POST request at $timestamp";
}
else {
print "Received request of type $method at $timestamp";
}
}
Receive POSTed File
sub handle_request {
my ($self, $cgi) = @_;
my $method = $ENV{REQUEST_METHOD};
if (lc($method) == 'post') {
my $file = $cgi->param('POSTDATA');
open OUT '>file.xml' or warn $!;
print OUT $file;
close OUT;
print 'Received data.';
}
else {
print 'Please POST a file.';
}
}
I've used the above code to handle rather large files (over 100 megs), but I strongly suspect that it wouldn't scale to multiple requests, but for a simple backend service the above code actually--somewhat to my surprise--behaves nicely.
Forking a Process for Requests
sub handle_request {
my ($self, $cgi) = @_;
my $pid = fork();
if ($pid == 0) {
exec('perl some_other_script.pl');
}
print 'Forked additional process.';
}
I think this approach is helpful, because it makes it easier to use a simple Perl server to manage your other processes. For example, you might write an XML parser in Java or C for speed, and then save a posted file (like the above example) and then fork a new process to run the parsing script.
Fin
All in all, HTTP::Server::Simple has been another very positive CPAN experience for me, and it is definitely the CPAN experience that is keeping me learning and playing with Perl. I do find the quality of documentation to be rather uneven (XML::Twig has an insurmountably large amount of documentation), and there are many fewer people randomly writing blog entries about Perl module X than you'd find in the Python or Ruby spheres. Hopefully this entry will turn up in some lost soul's search results.