rlucas.net: The Next Generation Rotating Header Image

November, 2009:

CGI.pm hangs in read_from_client under Catalyst / TT

Aahhh.  The joys of a Monday evening with the Perl debugger.

I have a quite simple Web front-end to a relatively complex back-end system.  The Web app is written in Perl with Catalyst as a framework, and Template Toolkit (TT) as the templating engine.  Since we are smart-asses, we aped the Ruby on Rails “link_to” function, intending to use it within our templates.  Seems simple enough; and originally, we used CGI.pm‘s escapeHTML function to handle the anchor text escaping.  Old habits (using the ’90s era CGI mod) die hard, I guess.

# in our Catalyst root; we use CGI.pm
use CGI qw/escapeHTML/; #OOPS
# ...
# mimics the Rails "link_to", and can be used directly in template toolkit
sub link_to {
 my $self = shift;
 my $anchor_text = escapeHTML(+shift); #ANOTHER OOPS HERE
 my @args = @_;
 my $uri = $self->uri_for(@args);
 return '<a href="' . $uri . '">' . $anchor_text . '</a>';

Well, as it turns out, I was getting a particularly maddening behavior whereby my app, when loaded in a single-process server instance, AND when a login action is the very first POST submitted (as is meet, right, and good), would inexplicably hang.  Not being intimately familiar with Catalyst, and being generally irate, I muttered a curse at the choice of framework and assumed that my contractors must have enabled some kind of recursion or infinite loop.  Nope; even ensuring that View::TT had RECURSION => 1 and a reasonable WHILE_MAX, I still got the hang.

So, into the debugger I dove.  A couple hours later, I’d tracked it down:

DB<6> CGI::read_from_client((eval 505)[/usr/share/perl/5.8/CGI.pm:895]:5):
5:          return $MOD_PERL
6:              ? $self->r->read($$buff, $len, $offset)
7:              : read(\*STDIN, $$buff, $len, $offset);

(Aside: why doesn’t WordPress have a “paste as code??”)

In CGI.pm’s “init,” it sets itself up to read from the browser input (on STDIN).  But if Catalyst has already slurped in STDIN, there’s nothing there to read, and CGI blocks.  (Either that, or something about CGI being init’ed from within the context of a Template that’s compiled and eval’ed under a mountain of cruft doesn’t sit right with CGI).

For the meantime, my solution is to use HTML::Entities and its encode_entities instead of CGI.  But beware using CGI when also using Catalyst; there’s a lot of dragons in CGI that you probably don’t want to have to understand and work around if you wake them up.  At least one other Catalyst / TT developer has had similar hang problems.  Better to use the equivalent functionality from cleaner, more predictable modules.

(Mad props to CGI and its rock star author, Lincoln D. Stein.  But any module that’s got workarounds for mosaic and mid-90’s server bugs has got baggage, period.)