C::C::WrapCGI - PATH_INFO and configurable cgi_dir
Rafael Kitover [Mon, 27 Apr 2009 05:24:45 +0000 (05:24 +0000)]
Changes
Makefile.PL
lib/Catalyst/Controller/CGIBin.pm
lib/Catalyst/Controller/WrapCGI.pm
t/cgibin.t
t/cgibin_root.t
t/lib/TestApp/Controller/Root.pm
t/lib/TestCGIBin/root/cgi-bin/pathinfo.pl [new file with mode: 0755]
t/lib/TestCGIBinRoot.pm
t/lib/TestCGIBinRoot/root/cgi/path/test.pl [moved from t/lib/TestCGIBinRoot/root/cgi-bin/path/test.pl with 100% similarity]
t/wrap-cgi.t

diff --git a/Changes b/Changes
index 721f40d..ba50373 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,7 +1,9 @@
 Revision history for Catalyst-Controller-WrapCGI
 
-    - configurable cgi_dir
-    - support for PATH_INFO
+0.0030  2009-04-27 05:17:17
+    - configurable cgi_dir (caelum)
+    - support for PATH_INFO and SCRIPT_NAME (caelum)
+    - FILEPATH_INFO (iffy implementation) (caelum)
 
 0.0029  2009-04-26 20:54:28
     - Allow more control over public paths to CGIBin actions (hdp)
index 869f036..9d7b089 100644 (file)
@@ -8,7 +8,7 @@ include  'Module::AutoInstall';
 requires 'Catalyst' => '5.7007';
 requires 'parent';
 requires 'Task::Weaken';
-requires 'HTTP::Request::AsCGI';
+requires 'HTTP::Request::AsCGI' => '0.7';;
 requires 'URI';
 requires 'File::Find::Rule';
 requires 'List::MoreUtils';
index 0277381..fd9b56c 100644 (file)
@@ -14,6 +14,7 @@ use Symbol 'gensym';
 use List::MoreUtils 'any';
 use IO::File ();
 use Carp;
+
 use namespace::clean -except => 'meta';
 
 =head1 NAME
@@ -22,11 +23,11 @@ Catalyst::Controller::CGIBin - Serve CGIs from root/cgi-bin
 
 =head1 VERSION
 
-Version 0.008
+Version 0.009
 
 =cut
 
-our $VERSION = '0.008';
+our $VERSION = '0.009';
 
 =head1 SYNOPSIS
 
@@ -46,6 +47,7 @@ In your .conf:
 
     <Controller::Foo>
         cgi_root_path cgi-bin
+        cgi_dir       cgi-bin
         <CGI>
             username_field username # used for REMOTE_USER env var
             pass_env PERL5LIB
@@ -59,21 +61,35 @@ In your .conf:
 Dispatches to CGI files in root/cgi-bin for /cgi-bin/ paths.
 
 Unlike L<ModPerl::Registry> this module does _NOT_ stat and recompile the CGI
-for every invocation. If this is something you need, let me know.
+for every invocation. This may be supported in the future if there's interest.
 
-CGI paths are converted into action names using cgi_action (below.)
+CGI paths are converted into action names using L</cgi_action>.
 
 Inherits from L<Catalyst::Controller::WrapCGI>, see the documentation for that
-module for configuration information.
+module for other configuration information.
+
+=head1 CONFIG PARAMS
+
+=head2 cgi_root_path
+
+The global URI path prefix for CGIs, defaults to C<cgi-bin/>.
+
+=head2 cgi_dir
+
+Path from which to read CGI files. Can be relative to C<$MYAPP_HOME/root> or
+absolute.  Defaults to C<$MYAPP_HOME/root/cgi-bin>.
 
 =cut
 
 has cgi_root_path => (is => 'ro', isa => 'Str', default => 'cgi-bin');
+has cgi_dir       => (is => 'ro', isa => 'Str', default => 'cgi-bin');
 
 sub register_actions {
     my ($self, $app) = @_;
 
-    my $cgi_bin = $app->path_to('root', 'cgi-bin');
+    my $cgi_bin = File::Spec->file_name_is_absolute($self->cgi_dir) ?
+        $self->cgi_dir
+        : $app->path_to('root', $self->cgi_dir);
 
     my $namespace = $self->action_namespace($app);
 
@@ -83,12 +99,13 @@ sub register_actions {
         my $cgi_path = abs2rel($file, $cgi_bin);
 
         next if any { $_ eq '.svn' } splitdir $cgi_path;
+        next if $cgi_path =~ /\.swp\z/;
 
         my $path        = join '/' => splitdir($cgi_path);
         my $action_name = $self->cgi_action($path);
         my $public_path = $self->cgi_path($path);
         my $reverse     = $namespace ? "$namespace/$action_name" : $action_name;
-        my $attrs       = { Path => [ $public_path ], Args => [ 0 ] };
+        my $attrs       = { Path => [ $public_path ] };
 
         my ($cgi, $type);
 
@@ -122,15 +139,22 @@ sub register_actions {
 
     $self->next::method($app, @_);
 
-# Tell Static::Simple to ignore the cgi-bin dir.
-    if (!any{ $_ eq 'cgi-bin' } @{ $app->config->{static}{ignore_dirs}||[] }) {
-        push @{ $app->config->{static}{ignore_dirs} }, 'cgi-bin';
+# Tell Static::Simple to ignore cgi_dir
+    if ($cgi_bin =~ /^@{[ $app->path_to('root') ]}/) {
+        my $rel = File::Spec->abs2rel($cgi_bin, $app->path_to('root'));
+
+        if (!any { $_ eq $rel }
+                @{ $app->config->{static}{ignore_dirs}||[] }) {
+            push @{ $app->config->{static}{ignore_dirs} }, $rel;
+        }
     }
 }
 
 =head1 METHODS
 
-=head2 $self->cgi_action($cgi)
+=head2 cgi_action
+
+C<<$self->cgi_action($cgi)>>
 
 Takes a path to a CGI from C<root/cgi-bin> such as C<foo/bar.cgi> and returns
 the action name it is registered as. See L</DESCRIPTION> for a discussion on how
@@ -156,7 +180,9 @@ sub cgi_action {
     $action_name
 }
 
-=head2 $self->cgi_path($cgi)
+=head2 cgi_path
+
+C<<$self->cgi_path($cgi)>>
 
 Takes a path to a CGI from C<root/cgi-bin> such as C<foo/bar.cgi> and returns
 the public path it should be registered under.
@@ -174,15 +200,14 @@ sub cgi_path {
     return "$root/$cgi";
 }
 
-=head2 $self->is_perl_cgi($path)
+=head2 is_perl_cgi
+
+C<<$self->is_perl_cgi($path)>>
 
 Tries to figure out whether the CGI is Perl or not.
 
 If it's Perl, it will be inlined into a sub instead of being forked off, see
-wrap_perl_cgi (below.)
-
-If it's not doing what you expect, you might want to override it, and let me
-know as well!
+L</wrap_perl_cgi>.
 
 =cut
 
@@ -203,7 +228,9 @@ sub is_perl_cgi {
     $? >> 8 == 0
 }
 
-=head2 $self->wrap_perl_cgi($path, $action_name)
+=head2 wrap_perl_cgi
+
+C<<$self->wrap_perl_cgi($path, $action_name)>>
 
 Takes the path to a Perl CGI and returns a coderef suitable for passing to
 cgi_to_response (from L<Catalyst::Controller::WrapCGI>.)
@@ -215,7 +242,8 @@ This is similar to how L<ModPerl::Registry> works, but will only work for
 well-written CGIs. Otherwise, you may have to override this method to do
 something more involved (see L<ModPerl::PerlRun>.)
 
-Scripts with C<__DATA__> sections now work too.
+Scripts with C<__DATA__> sections now work too, as well as scripts that call
+C<exit()>.
 
 =cut
 
@@ -264,7 +292,9 @@ sub wrap_perl_cgi {
     $coderef
 }
 
-=head2 $self->wrap_nonperl_cgi($path, $action_name)
+=head2 wrap_nonperl_cgi
+
+C<<$self->wrap_nonperl_cgi($path, $action_name)>>
 
 Takes the path to a non-Perl CGI and returns a coderef for executing it.
 
@@ -282,6 +312,8 @@ sub wrap_nonperl_cgi {
     sub { system $cgi }
 }
 
+__PACKAGE__->meta->make_immutable;
+
 =head1 SEE ALSO
 
 L<Catalyst::Controller::WrapCGI>, L<CatalystX::GlobalContext>,
index 11f2fe7..262e960 100644 (file)
@@ -9,6 +9,9 @@ use HTTP::Request::AsCGI ();
 use HTTP::Request ();
 use URI ();
 use Catalyst::Exception ();
+use URI::Escape;
+
+use namespace::clean -except => 'meta';
 
 =head1 NAME
 
@@ -91,12 +94,14 @@ open my $REAL_STDOUT, ">>&=".fileno(*STDOUT);
 
 =head1 METHODS
 
-=head2 $self->cgi_to_response($c, $coderef)
+=head2 cgi_to_response
+
+C<<$self->cgi_to_response($c, $coderef)>>
 
 Does the magic of running $coderef in a CGI environment, and populating the
 appropriate parts of your Catalyst context with the results.
 
-Calls wrap_cgi (below.)
+Calls L</wrap_cgi>.
 
 =cut
 
@@ -119,7 +124,9 @@ sub cgi_to_response {
   $c->res->headers($res->headers);
 }
 
-=head2 $self->wrap_cgi($c, $coderef)
+=head2 wrap_cgi
+
+C<<$self->wrap_cgi($c, $coderef)>>
 
 Runs $coderef in a CGI environment using L<HTTP::Request::AsCGI>, returns an
 L<HTTP::Response>.
@@ -130,7 +137,7 @@ The environment variables to pass on are taken from the configuration for your
 Controller, see L</SYNOPSIS> for an example. If you don't supply a list of
 environment variables to pass, the whole of %ENV is used.
 
-Used by cgi_to_response (above), which is probably what you want to use as well.
+Used by L</cgi_to_response>, which is probably what you want to use as well.
 
 =cut
 
@@ -167,13 +174,15 @@ sub wrap_cgi {
                ? eval { $c->user->obj->$username_field }
                 : '');
 
-  my $path_info = '/'.join '/' => @{ $c->req->args };
+  my $path_info = '/'.join '/' => map uri_escape_utf8($_), @{ $c->req->args };
 
   my $env = HTTP::Request::AsCGI->new(
               $req,
               ($username ? (REMOTE_USER => $username) : ()),
               %$filtered_env,
-              PATH_INFO => $path_info
+              PATH_INFO => $path_info,
+              FILEPATH_INFO => '/'.$c->action.$path_info, # eww
+              SCRIPT_NAME => $c->uri_for($c->action)->path
             );
 
   {
@@ -235,6 +244,7 @@ sub _filtered_env {
   return { map {; $_ => $env->{$_} } @ok };
 }
 
+__PACKAGE__->meta->make_immutable;
 
 =head1 ACKNOWLEDGEMENTS
 
index 6e71007..910b5f0 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use FindBin '$Bin';
 use lib "$Bin/lib";
 
-use Test::More tests => 7;
+use Test::More tests => 8;
 
 use Catalyst::Test 'TestCGIBin';
 use HTTP::Request::Common;
@@ -54,6 +54,10 @@ $response = request '/my-bin/path/testdata.pl';
 is($response->content, "testing\n",
     'scripts with __DATA__ sections work');
 
+$response = request '/my-bin/pathinfo.pl/path/info';
+is($response->content, '/path/info',
+    'PATH_INFO works');
+
 SKIP: {
     skip "Can't run shell scripts on non-*nix", 1
         if $^O eq 'MSWin32' || $^O eq 'VMS';
index d112e28..876fbe7 100644 (file)
@@ -11,9 +11,9 @@ use Test::More tests => 1;
 use Catalyst::Test 'TestCGIBinRoot';
 use HTTP::Request::Common;
 
-# test default root of "cgi-bin"
+# Test configurable path root and dir
 
-my $response = request POST '/cgi-bin/path/test.pl', [
+my $response = request POST '/cgi/path/test.pl', [
     foo => 'bar',
     bar => 'baz'
 ];
index 5ade07b..c1729e7 100644 (file)
@@ -26,4 +26,24 @@ sub test_path_info : Path('/cgi-bin/test_pathinfo.cgi') {
     });
 }
 
+sub test_filepath_info : Path('/cgi-bin/test_filepathinfo.cgi') {
+    my ($self, $c) = @_;
+
+    $self->cgi_to_response($c, sub {
+        my $cgi = CGI->new;
+        print $cgi->header;
+        print $ENV{FILEPATH_INFO}
+    });
+}
+
+sub test_script_name : Path('/cgi-bin/test_scriptname.cgi') {
+    my ($self, $c) = @_;
+
+    $self->cgi_to_response($c, sub {
+        my $cgi = CGI->new;
+        print $cgi->header;
+        print $ENV{SCRIPT_NAME}
+    });
+}
+
 1;
diff --git a/t/lib/TestCGIBin/root/cgi-bin/pathinfo.pl b/t/lib/TestCGIBin/root/cgi-bin/pathinfo.pl
new file mode 100755 (executable)
index 0000000..497d68d
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/perl 
+
+use strict;
+use warnings;
+
+use CGI ':standard';
+
+print header;
+print $ENV{PATH_INFO};
index cea069e..4414cb9 100644 (file)
@@ -3,6 +3,13 @@ package TestCGIBinRoot;
 use Catalyst::Runtime '5.70';
 use parent 'Catalyst';
 
+__PACKAGE__->config({
+    Controller::CGIHandler => {
+        cgi_root_path => 'cgi',
+        cgi_dir => 'cgi'
+    }
+});
+
 __PACKAGE__->setup(qw/Static::Simple/);
 
 1;
index 2477c7d..aa253cf 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use FindBin '$Bin';
 use lib "$Bin/lib";
 
-use Test::More tests => 2;
+use Test::More tests => 4;
 
 use Catalyst::Test 'TestApp';
 use HTTP::Request::Common;
@@ -18,6 +18,13 @@ my $response = request POST '/cgi-bin/test.cgi', [
 
 is($response->content, 'foo:bar bar:baz', 'POST to CGI');
 
-$response = request '/cgi-bin/test_pathinfo.cgi/path/info';
+$response = request '/cgi-bin/test_pathinfo.cgi/path/%2Finfo';
+is($response->content, '/path//info', 'PATH_INFO is correct');
 
-is($response->content, '/path/info', 'PATH_INFO is correct');
+$response = request '/cgi-bin/test_filepathinfo.cgi/path/%2Finfo';
+is($response->content, '/test_filepath_info/path//info',
+    'FILEPATH_INFO is correct (maybe)');
+
+$response = request '/cgi-bin/test_scriptname.cgi/foo/bar';
+is($response->content, '/cgi-bin/test_scriptname.cgi',
+    'SCRIPT_NAME is correct');