override exit() for Perl cgis in cgi-bin
Hans Dieter Pearcey [Sun, 26 Apr 2009 00:37:09 +0000 (00:37 +0000)]
set $0 when executing a Perl cgi

Changes
lib/Catalyst/Controller/CGIBin.pm
t/cgibin.t
t/lib/TestCGIBin/root/cgi-bin/exit.pl [new file with mode: 0644]

diff --git a/Changes b/Changes
index b897206..d402922 100644 (file)
--- a/Changes
+++ b/Changes
@@ -28,3 +28,6 @@ Revision history for Catalyst-Controller-WrapCGI
 
 0.0029
     Allow more control over public paths to CGIBin actions.
+    Override exit() in CGIBin-wrapped Perl cgis to avoid terminating the
+    Catalyst process.
+    Set (temporarily) $0 to the filename of the Perl cgi being executed.
index 5859ad8..30f44ee 100644 (file)
@@ -218,12 +218,33 @@ sub wrap_perl_cgi {
 
     my $coderef = do {
         no warnings;
+        # catch exit() and turn it into (effectively) a return
+        # we *must* eval STRING because the code needs to be compiled with the
+        # overridden CORE::GLOBAL::exit in view
+        #
+        # set $0 to the name of the cgi file in case it's used there
         eval ' 
+            my $cgi_exited = "EXIT\n";
+            BEGIN { *CORE::GLOBAL::exit = sub (;$) {
+                die [ $cgi_exited, $_[0] || 0 ];
+            } }
             package Catalyst::Controller::CGIBin::_CGIs_::'.$action_name.';
             sub {'
                 . 'local *DATA;'
                 . q{open DATA, '<', \$data;}
+                . qq{local \$0 = "\Q$cgi\E";}
+                . q/my $rv = eval {/
                 . $code
+                . q/};/
+                . q{
+                    return $rv unless $@;
+                    die $@ if $@ and not (
+                      ref($@) eq 'ARRAY' and
+                      $@->[0] eq $cgi_exited
+                    );
+                    die "exited nonzero: $@->[1]" if $@->[1] != 0;
+                    return $rv;
+                }
          . '}';
     };
 
index 8340633..0a89f26 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use FindBin '$Bin';
 use lib "$Bin/lib";
 
-use Test::More tests => 4;
+use Test::More tests => 6;
 
 use Catalyst::Test 'TestCGIBin';
 use HTTP::Request::Common;
@@ -21,6 +21,19 @@ my $response = request POST '/my-bin/path/test.pl', [
 
 is($response->content, 'foo:bar bar:baz', 'POST to Perl CGI File');
 
+$response = request POST '/my-bin/exit.pl', [
+    name => 'world',
+];
+
+is($response->content, 'hello world', 'POST to Perl CGI with exit()');
+
+$response = request POST '/my-bin/exit.pl', [
+    name => 'world',
+    exit => 17,
+];
+
+is($response->code, 500, 'POST to Perl CGI with nonzero exit()');
+
 $response = request POST '/cgihandler/dongs', [
     foo => 'bar',
     bar => 'baz'
diff --git a/t/lib/TestCGIBin/root/cgi-bin/exit.pl b/t/lib/TestCGIBin/root/cgi-bin/exit.pl
new file mode 100644 (file)
index 0000000..60662c3
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use CGI ':standard';
+
+print header;
+print "hello " . param('name');
+exit(param('exit') || 0);