Instead of updating 60+ scripts to read e.g.
$session = get_session() or Site::CGI->go_login;I decided to get hackier.
I decided to tell the core library that it was in FastCGI mode after loading it in the FCGI dispatcher. When an access check fails in FastCGI mode, the regular CGI logic is replaced by sending a signal back to the dispatcher.
There's only one way to jump stack frames in Perl, and that's with
diejumping out to the nearest
eval(and no other). It's adequate for exception handling, but not superb. The dispatcher has an
evalblock, of course, but the death would be intercepted by any other
evalblocks between the access check and the dispatcher. Furthermore, any other kinds of death will be received by the dispatcher, and it must somehow reliably determine the difference between "ordinary access denied signal" and "something horrible went wrong, and this message should be logged."
In Ruby, there are a handful of choices for this: besides continuations or maybe an epic hack around blocks, there's the
catchchannel for non-exception signals, and the only thing to worry about there is a naming convention to avoid clashes.
In many other languages, there's a fuller exception system, which doesn't require intermediate levels to receive exceptions they can't understand. (They can, by catching the base exception type; but then, you can be reasonably expected to rethrow once you're done with it.)
In Perl, there is just
die, which has all sorts of well-known problems, such as possibly stomping on the global
evalis effectively always catching the base exception type.
Thus, my dispatcher sets the response up in Site::CGI, calls the handler, and down there in the bottom of
Site::CGI->exit_with_template(403) if $_isFCGI;. That uses the response to set the
cgi-error/403.htmltemplate, then dies with a new Site::CGI object, which the dispatcher can recognize. For the other half of the problem, it turns out there are no intermediate layers because the handlers want to let failure propagate to the dispatcher.
Spooky action at a distance, coordinating effort across four files. I don't expect anyone else to understand it.