X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst.pm;h=1b47918ad1ee3708386a9e5e8e7be9db81c43a62;hb=2328d24b6808f6d51f7338858bed5d0f12b95821;hp=3a8b81656dbdc3ff343f3f958cb68cf1108e2b6d;hpb=f147e6c23995d0beac4bd74cc17dbcfd1f710190;p=catagits%2FCatalyst-Runtime.git
diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm
index 3a8b816..1b47918 100644
--- a/lib/Catalyst.pm
+++ b/lib/Catalyst.pm
@@ -15,9 +15,7 @@ use Catalyst::Response;
use Catalyst::Utils;
use Catalyst::Controller;
use Data::OptList;
-use Devel::InnerPackage ();
use File::stat;
-use Module::Pluggable::Object ();
use Text::SimpleTable ();
use Path::Class::Dir ();
use Path::Class::File ();
@@ -29,8 +27,15 @@ use Tree::Simple::Visitor::FindByUID;
use Class::C3::Adopt::NEXT;
use List::MoreUtils qw/uniq/;
use attributes;
+use String::RewritePrefix;
+use Catalyst::EngineLoader;
use utf8;
use Carp qw/croak carp shortmess/;
+use Try::Tiny;
+use Plack::Middleware::Conditional;
+use Plack::Middleware::ReverseProxy;
+use Plack::Middleware::IIS6ScriptNameFix;
+use Plack::Middleware::LighttpdScriptNameFix;
BEGIN { require 5.008004; }
@@ -68,18 +73,17 @@ our $GO = Catalyst::Exception::Go->new;
#maybe we should just make them attributes with a default?
__PACKAGE__->mk_classdata($_)
for qw/container arguments dispatcher engine log dispatcher_class
- engine_class context_class request_class response_class stats_class
- setup_finished/;
+ engine_loader context_class request_class response_class stats_class
+ setup_finished _psgi_app loading_psgi_file/;
__PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
-__PACKAGE__->engine_class('Catalyst::Engine::CGI');
__PACKAGE__->request_class('Catalyst::Request');
__PACKAGE__->response_class('Catalyst::Response');
__PACKAGE__->stats_class('Catalyst::Stats');
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.80032';
+our $VERSION = '5.90002';
sub import {
my ( $class, @arguments ) = @_;
@@ -566,13 +570,7 @@ If you want to search for controllers, pass in a regexp as the argument.
=cut
-sub controller {
- my ( $c, $name, @args ) = @_;
-
- $name ||= Catalyst::Utils::class2classshortsuffix( $c->action->class );
-
- return $c->container->get_component_from_sub_container( 'controller', $name, $c, @args);
-}
+sub controller { shift->_lookup_mvc('controller', @_) }
=head2 $c->model($name)
@@ -595,20 +593,7 @@ If you want to search for models, pass in a regexp as the argument.
=cut
-sub model {
- my ( $c, $name, @args ) = @_;
-
- if (ref $c && !$name) {
- return $c->stash->{current_model_instance}
- if $c->stash->{current_model_instance};
-
- $name = $c->stash->{current_model}
- if $c->stash->{current_model};
- }
-
- return $c->container->get_component_from_sub_container( 'model', $name, $c, @args);
-}
-
+sub model { shift->_lookup_mvc('model', @_) }
=head2 $c->view($name)
@@ -631,18 +616,23 @@ If you want to search for views, pass in a regexp as the argument.
=cut
-sub view {
- my ( $c, $name, @args ) = @_;
+sub view { shift->_lookup_mvc('view', @_) }
+
+sub _lookup_mvc {
+ my ( $c, $type, $name, @args ) = @_;
if (ref $c && !$name) {
- return $c->stash->{current_view_instance}
- if $c->stash->{current_view_instance};
+ my $current_instance = $c->stash->{"current_${type}_instance"};
+ return $current_instance
+ if $current_instance && $type ne 'controller';
- $name = $c->stash->{current_view}
- if $c->stash->{current_view};
+ $name = $type eq 'controller'
+ ? Catalyst::Utils::class2classshortsuffix($c->action->class)
+ : $c->stash->{"current_${type}"}
+ ;
}
- return $c->container->get_component_from_sub_container( 'view', $name, $c, @args);
+ return $c->container->get_component_from_sub_container($type, $name, $c, @args);
}
=head2 $c->controllers
@@ -970,7 +960,10 @@ sub setup {
$class->setup_log( delete $flags->{log} );
$class->setup_plugins( delete $flags->{plugins} );
$class->setup_dispatcher( delete $flags->{dispatcher} );
- $class->setup_engine( delete $flags->{engine} );
+ if (my $engine = delete $flags->{engine}) {
+ $class->log->warn("Specifying the engine in ->setup is no longer supported, see Catalyst::Upgrading");
+ }
+ $class->setup_engine();
$class->setup_stats( delete $flags->{stats} );
for my $flag ( sort keys %{$flags} ) {
@@ -1033,11 +1026,11 @@ EOF
if (
$class->debug and
- my @comps_types = $class->container->get_components_types
+ my $comps = $class->container->get_all_components($class)
) {
my $column_width = Catalyst::Utils::term_width() - 8 - 9;
my $t = Text::SimpleTable->new( [ $column_width, 'Class' ], [ 8, 'Type' ] );
- $t->row( @$_ ) for @comps_types;
+ $t->row( $_ => ref($comps->{$_}) ? 'instance' : 'class' ) for keys %$comps;
$class->log->debug( "Loaded components:\n" . $t->draw . "\n" );
}
@@ -1442,22 +1435,16 @@ Returns a hash of components.
sub components {
my ( $class, $comps ) = @_;
- # FIXME: should this ugly kludge exist?
- $class->setup_config unless defined $class->container;
+ # people create components calling this sub directly, before setup
+ $class->setup_config unless $class->container;
- my $containers;
- $containers->{$_} = $class->container->get_sub_container($_)
- for qw(model view controller);
+ my $container = $class->container;
if ( $comps ) {
- for my $component ( keys %$comps ) {
- $class->container->add_component(
- $component, $class->setup_component($component)
- );
- }
+ $container->add_component( $_ ) for keys %$comps;
}
- return $class->container->get_all_components();
+ return $container->get_all_components($class);
}
=head2 $c->context_class
@@ -1726,6 +1713,7 @@ sub finalize_headers {
$response->body(
qq{
This item has moved here.
}
);
+ $response->content_type('text/html; charset=utf-8');
}
}
@@ -1735,9 +1723,9 @@ sub finalize_headers {
# get the length from a filehandle
if ( blessed( $response->body ) && $response->body->can('read') || ref( $response->body ) eq 'GLOB' )
{
- my $stat = stat $response->body;
- if ( $stat && $stat->size > 0 ) {
- $response->content_length( $stat->size );
+ my $size = -s $response->body;
+ if ( $size ) {
+ $response->content_length( $size );
}
else {
$c->log->warn('Serving filehandle without a content-length');
@@ -1811,7 +1799,7 @@ sub handle_request {
# Always expect worst case!
my $status = -1;
- eval {
+ try {
if ($class->debug) {
my $secs = time - $START || 1;
my $av = sprintf '%.3f', $COUNT / $secs;
@@ -1822,12 +1810,11 @@ sub handle_request {
my $c = $class->prepare(@arguments);
$c->dispatch;
$status = $c->finalize;
- };
-
- if ( my $error = $@ ) {
- chomp $error;
- $class->log->error(qq/Caught exception in engine "$error"/);
}
+ catch {
+ chomp(my $error = $_);
+ $class->log->error(qq/Caught exception in engine "$error"/);
+ };
$COUNT++;
@@ -1864,28 +1851,38 @@ sub prepare {
$c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
}
- #XXX reuse coderef from can
- # Allow engine to direct the prepare flow (for POE)
- if ( $c->engine->can('prepare') ) {
- $c->engine->prepare( $c, @arguments );
- }
- else {
- $c->prepare_request(@arguments);
- $c->prepare_connection;
- $c->prepare_query_parameters;
- $c->prepare_headers;
- $c->prepare_cookies;
- $c->prepare_path;
-
- # Prepare the body for reading, either by prepare_body
- # or the user, if they are using $c->read
- $c->prepare_read;
-
- # Parse the body unless the user wants it on-demand
- unless ( ref($c)->config->{parse_on_demand} ) {
- $c->prepare_body;
+ try {
+ # Allow engine to direct the prepare flow (for POE)
+ if ( my $prepare = $c->engine->can('prepare') ) {
+ $c->engine->$prepare( $c, @arguments );
+ }
+ else {
+ $c->prepare_request(@arguments);
+ $c->prepare_connection;
+ $c->prepare_query_parameters;
+ $c->prepare_headers;
+ $c->prepare_cookies;
+ $c->prepare_path;
+
+ # Prepare the body for reading, either by prepare_body
+ # or the user, if they are using $c->read
+ $c->prepare_read;
+
+ # Parse the body unless the user wants it on-demand
+ unless ( ref($c)->config->{parse_on_demand} ) {
+ $c->prepare_body;
+ }
}
}
+ # VERY ugly and probably shouldn't rely on ->finalize actually working
+ catch {
+ # failed prepare is always due to an invalid request, right?
+ $c->response->status(400);
+ $c->response->content_type('text/plain');
+ $c->response->body('Bad Request');
+ $c->finalize;
+ die $_;
+ };
my $method = $c->req->method || '';
my $path = $c->req->path;
@@ -2264,7 +2261,12 @@ Starts the engine.
=cut
-sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
+sub run {
+ my $app = shift;
+ $app->engine_loader->needs_psgi_engine_compat_hack ?
+ $app->engine->run($app, @_) :
+ $app->engine->run( $app, $app->_finalized_psgi_app, @_ );
+}
=head2 $c->set_action( $action, $code, $namespace, $attrs )
@@ -2291,15 +2293,20 @@ sub setup_config {
my %args = %{ $class->config || {} };
- my @container_classes = ( "${class}::Container", 'Catalyst::IOC::Container');
- unshift @container_classes, delete $args{container_class} if exists $args{container_class};
+ my $container_class;
- my $container_class = Class::MOP::load_first_existing_class(@container_classes);
+ if ( exists $args{container_class} ) {
+ $container_class = delete $args{container_class};
+ Class::MOP::load_class($container_class);
+ }
+ else {
+ $container_class = Class::MOP::load_first_existing_class("${class}::Container", 'Catalyst::IOC::Container');
+ }
- my $container = $container_class->new( %args, name => "$class" );
+ my $container = $container_class->new( %args, application_name => "$class", name => "$class" );
$class->container($container);
- my $config = $container->resolve(service => 'config');
+ my $config = $container->resolve( service => 'config' );
$class->config($config);
$class->finalize_config; # back-compat
}
@@ -2315,145 +2322,29 @@ sub finalize_config { }
This method is called internally to set up the application's components.
It finds modules by calling the L method, expands them to
-package names with the L method, and then installs
-each component into the application.
+package names with the $container->expand_component_module method, and then
+installs each component into the application.
The C config option is passed to both of the above methods.
-Installation of each component is performed by the L method,
-below.
-
=cut
-sub setup_components {
- my $class = shift;
-
- my $config = $class->config->{ setup_components };
-
- Catalyst::Exception->throw(
- qq{You are using search_extra config option. That option is\n} .
- qq{deprecated, please refer to the documentation for\n} .
- qq{other ways of achieving the same results.\n}
- ) if delete $config->{ search_extra };
-
- my @comps = $class->locate_components($config);
- my %comps = map { $_ => 1 } @comps;
+sub setup_components { shift->container->setup_components }
- my $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @comps;
- $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
- qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
- ) if $deprecatedcatalyst_component_names;
-
- for my $component ( @comps ) {
-
- # We pass ignore_loaded here so that overlay files for (e.g.)
- # Model::DBI::Schema sub-classes are loaded - if it's in @comps
- # we know M::P::O found a file on disk so this is safe
-
- Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
- }
-
- for my $component (@comps) {
- my $instance = $class->setup_component($component);
- $class->container->add_component( $component, $instance );
- my @expanded_components = $instance->can('expand_modules')
- ? $instance->expand_modules( $component, $config )
- : $class->expand_component_module( $component, $config );
- for my $component (@expanded_components) {
- next if $comps{$component};
-
- $deprecatedcatalyst_component_names = grep { /::[CMV]::/ } @expanded_components;
- $class->log->warn(qq{Your application is using the deprecated ::[MVC]:: type naming scheme.\n}.
- qq{Please switch your class names to ::Model::, ::View:: and ::Controller: as appropriate.\n}
- ) if $deprecatedcatalyst_component_names;
-
- $class->container->add_component( $component, $class->setup_component($component) );
- }
- }
-
- $class->container->get_sub_container('model')->make_single_default;
- $class->container->get_sub_container('view')->make_single_default;
-}
-
-
-=head2 $c->locate_components( $setup_component_config )
-
-This method is meant to provide a list of component modules that should be
-setup for the application. By default, it will use L.
-
-Specify a C config option to pass additional options directly
-to L.
+=head2 locate_components
=cut
sub locate_components {
- my $class = shift;
- my $config = shift;
-
- my @paths = qw( ::Controller ::C ::Model ::M ::View ::V );
+ my $class = shift;
- my $locator = Module::Pluggable::Object->new(
- search_path => [ map { s/^(?=::)/$class/; $_; } @paths ],
- %$config
- );
+ $class->log->warn('The locate_components method has been deprecated.');
+ $class->log->warn('Please read Catalyst::IOC::Container documentation to');
+ $class->log->warn('update your application.');
# XXX think about ditching this sort entirely
- my @comps = sort { length $a <=> length $b } $locator->plugins;
-
- return @comps;
-}
-
-=head2 $c->expand_component_module( $component, $setup_component_config )
-
-Components found by C will be passed to this method, which
-is expected to return a list of component (package) names to be set up.
-
-=cut
-
-sub expand_component_module {
- my ($class, $module) = @_;
- return Devel::InnerPackage::list_packages( $module );
-}
-
-=head2 $c->setup_component
-
-=cut
-
-## FIXME - Why the hell do we try calling the ->COMPONENT method twice, this is madness!?!
-sub setup_component {
- my( $class, $component ) = @_;
-
- unless ( $component->can( 'COMPONENT' ) ) {
- return $component;
- }
-
- my $suffix = Catalyst::Utils::class2classsuffix( $component );
- my $config = $class->config->{ $suffix } || {};
- # Stash catalyst_component_name in the config here, so that custom COMPONENT
- # methods also pass it. local to avoid pointlessly shitting in config
- # for the debug screen, as $component is already the key name.
- local $config->{catalyst_component_name} = $component;
-
- my $instance = eval { $component->COMPONENT( $class, $config ); };
-
- if ( my $error = $@ ) {
- chomp $error;
- Catalyst::Exception->throw(
- message => qq/Couldn't instantiate component "$component", "$error"/
- );
- }
- elsif (!blessed $instance) {
- my $metaclass = Moose::Util::find_meta($component);
- my $method_meta = $metaclass->find_method_by_name('COMPONENT');
- my $component_method_from = $method_meta->associated_metaclass->name;
- my $value = defined($instance) ? $instance : 'undef';
- Catalyst::Exception->throw(
- message =>
- qq/Couldn't instantiate component "$component", COMPONENT() method (from $component_method_from) didn't return an object-like value (value was $value)./
- );
- }
-
- return $instance;
+ return sort { length $a <=> length $b }
+ @{ $class->container->resolve( service => 'locate_components' ) };
}
=head2 $c->setup_dispatcher
@@ -2489,114 +2380,156 @@ Sets up engine.
=cut
+sub engine_class {
+ my ($class, $requested_engine) = @_;
+
+ if (!$class->engine_loader || $requested_engine) {
+ $class->engine_loader(
+ Catalyst::EngineLoader->new({
+ application_name => $class,
+ (defined $requested_engine
+ ? (requested_engine => $requested_engine) : ()),
+ }),
+ );
+ }
+ $class->engine_loader->catalyst_engine_class;
+}
+
sub setup_engine {
- my ( $class, $engine ) = @_;
+ my ($class, $requested_engine) = @_;
+
+ my $engine = $class->engine_class($requested_engine);
+
+ # Don't really setup_engine -- see _setup_psgi_app for explanation.
+ return if $class->loading_psgi_file;
+
+ Class::MOP::load_class($engine);
+
+ if ($ENV{MOD_PERL}) {
+ my $apache = $class->engine_loader->auto;
+
+ my $meta = find_meta($class);
+ my $was_immutable = $meta->is_immutable;
+ my %immutable_options = $meta->immutable_options;
+ $meta->make_mutable if $was_immutable;
- if ($engine) {
- $engine = 'Catalyst::Engine::' . $engine;
+ $meta->add_method(handler => sub {
+ my $r = shift;
+ my $psgi_app = $class->psgi_app;
+ $apache->call_app($r, $psgi_app);
+ });
+
+ $meta->make_immutable(%immutable_options) if $was_immutable;
}
- if ( my $env = Catalyst::Utils::env_value( $class, 'ENGINE' ) ) {
- $engine = 'Catalyst::Engine::' . $env;
+ $class->engine( $engine->new );
+
+ return;
+}
+
+sub _finalized_psgi_app {
+ my ($app) = @_;
+
+ unless ($app->_psgi_app) {
+ my $psgi_app = $app->_setup_psgi_app;
+ $app->_psgi_app($psgi_app);
}
- if ( $ENV{MOD_PERL} ) {
- my $meta = Class::MOP::get_metaclass_by_name($class);
+ return $app->_psgi_app;
+}
- # create the apache method
- $meta->add_method('apache' => sub { shift->engine->apache });
+sub _setup_psgi_app {
+ my ($app) = @_;
- my ( $software, $version ) =
- $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
+ for my $home (Path::Class::Dir->new($app->config->{home})) {
+ my $psgi_file = $home->file(
+ Catalyst::Utils::appprefix($app) . '.psgi',
+ );
- $version =~ s/_//g;
- $version =~ s/(\.[^.]+)\./$1/g;
+ next unless -e $psgi_file;
- if ( $software eq 'mod_perl' ) {
+ # If $psgi_file calls ->setup_engine, it's doing so to load
+ # Catalyst::Engine::PSGI. But if it does that, we're only going to
+ # throw away the loaded PSGI-app and load the 5.9 Catalyst::Engine
+ # anyway. So set a flag (ick) that tells setup_engine not to populate
+ # $c->engine or do any other things we might regret.
- if ( !$engine ) {
+ $app->loading_psgi_file(1);
+ my $psgi_app = Plack::Util::load_psgi($psgi_file);
+ $app->loading_psgi_file(0);
- if ( $version >= 1.99922 ) {
- $engine = 'Catalyst::Engine::Apache2::MP20';
- }
+ return $psgi_app
+ unless $app->engine_loader->needs_psgi_engine_compat_hack;
- elsif ( $version >= 1.9901 ) {
- $engine = 'Catalyst::Engine::Apache2::MP19';
- }
+ warn <<"EOW";
+Found a legacy Catalyst::Engine::PSGI .psgi file at ${psgi_file}.
- elsif ( $version >= 1.24 ) {
- $engine = 'Catalyst::Engine::Apache::MP13';
- }
+Its content has been ignored. Please consult the Catalyst::Upgrading
+documentation on how to upgrade from Catalyst::Engine::PSGI.
+EOW
+ }
- else {
- Catalyst::Exception->throw( message =>
- qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
- }
+ return $app->apply_default_middlewares($app->psgi_app);
+}
- }
+=head2 $c->apply_default_middlewares
- # install the correct mod_perl handler
- if ( $version >= 1.9901 ) {
- *handler = sub : method {
- shift->handle_request(@_);
- };
- }
- else {
- *handler = sub ($$) { shift->handle_request(@_) };
- }
+Adds the following L middlewares to your application, since they are
+useful and commonly needed:
- }
+L, (conditionally added based on the status
+of your $ENV{REMOTE_ADDR}, and can be forced on with C
+or forced off with C), L
+(if you are using Lighttpd), L (always
+applied since this middleware is smart enough to conditionally apply itself).
- elsif ( $software eq 'Zeus-Perl' ) {
- $engine = 'Catalyst::Engine::Zeus';
- }
+Additionally if we detect we are using Nginx, we add a bit of custom middleware
+to solve some problems with the way that server handles $ENV{PATH_INFO} and
+$ENV{SCRIPT_NAME}
- else {
- Catalyst::Exception->throw(
- message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
- }
- }
+=cut
- unless ($engine) {
- $engine = $class->engine_class;
- }
- Class::MOP::load_class($engine);
+sub apply_default_middlewares {
+ my ($app, $psgi_app) = @_;
- # check for old engines that are no longer compatible
- my $old_engine;
- if ( $engine->isa('Catalyst::Engine::Apache')
- && !Catalyst::Engine::Apache->VERSION )
- {
- $old_engine = 1;
- }
+ $psgi_app = Plack::Middleware::Conditional->wrap(
+ $psgi_app,
+ builder => sub { Plack::Middleware::ReverseProxy->wrap($_[0]) },
+ condition => sub {
+ my ($env) = @_;
+ return if $app->config->{ignore_frontend_proxy};
+ return $env->{REMOTE_ADDR} eq '127.0.0.1'
+ || $app->config->{using_frontend_proxy};
+ },
+ );
- elsif ( $engine->isa('Catalyst::Engine::Server::Base')
- && Catalyst::Engine::Server->VERSION le '0.02' )
- {
- $old_engine = 1;
- }
+ # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME
+ # http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html
+ $psgi_app = Plack::Middleware::LighttpdScriptNameFix->wrap($psgi_app);
- elsif ($engine->isa('Catalyst::Engine::HTTP::POE')
- && $engine->VERSION eq '0.01' )
- {
- $old_engine = 1;
- }
+ # we're applying this unconditionally as the middleware itself already makes
+ # sure it doesn't fuck things up if it's not running under one of the right
+ # IIS versions
+ $psgi_app = Plack::Middleware::IIS6ScriptNameFix->wrap($psgi_app);
- elsif ($engine->isa('Catalyst::Engine::Zeus')
- && $engine->VERSION eq '0.01' )
- {
- $old_engine = 1;
- }
+ return $psgi_app;
+}
- if ($old_engine) {
- Catalyst::Exception->throw( message =>
- qq/Engine "$engine" is not supported by this version of Catalyst/
- );
- }
+=head2 $c->psgi_app
- # engine instance
- $class->engine( $engine->new );
+Returns a PSGI application code reference for the catalyst application
+C<$c>. This is the bare application without any middlewares
+applied. C<${myapp}.psgi> is not taken into account.
+
+This is what you want to be using to retrieve the PSGI application code
+reference of your Catalyst application for use in F<.psgi> files.
+
+=cut
+
+sub psgi_app {
+ my ($app) = @_;
+ return $app->engine->build_psgi_app($app);
}
=head2 $c->setup_home
@@ -2868,8 +2801,46 @@ to be shown in hit debug tables in the test server.
=item *
C - Controlls if the C or C environment
-variable should be used for determining the request path. See L
-for more information.
+variable should be used for determining the request path.
+
+Most web server environments pass the requested path to the application using environment variables,
+from which Catalyst has to reconstruct the request base (i.e. the top level path to / in the application,
+exposed as C<< $c->request->base >>) and the request path below that base.
+
+There are two methods of doing this, both of which have advantages and disadvantages. Which method is used
+is determined by the C<< $c->config(use_request_uri_for_path) >> setting (which can either be true or false).
+
+=over
+
+=item use_request_uri_for_path => 0
+
+This is the default (and the) traditional method that Catalyst has used for determining the path information.
+The path is synthesised from a combination of the C and C environment variables.
+The allows the application to behave correctly when C is being used to redirect requests
+into the application, as these variables are adjusted by mod_rewrite to take account for the redirect.
+
+However this method has the major disadvantage that it is impossible to correctly decode some elements
+of the path, as RFC 3875 says: "C<< Unlike a URI path, the PATH_INFO is not URL-encoded, and cannot
+contain path-segment parameters. >>" This means PATH_INFO is B decoded, and therefore Catalyst
+can't distinguish / vs %2F in paths (in addition to other encoded values).
+
+=item use_request_uri_for_path => 1
+
+This method uses the C and C environment variables. As C is never
+decoded, this means that applications using this mode can correctly handle URIs including the %2F character
+(i.e. with C set to C in Apache).
+
+Given that this method of path resolution is provably more correct, it is recommended that you use
+this unless you have a specific need to deploy your application in a non-standard environment, and you are
+aware of the implications of not being able to handle encoded URI paths correctly.
+
+However it also means that in a number of cases when the app isn't installed directly at a path, but instead
+is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html directory, with mod_rewrite in a
+.htaccess file, or when SSI is used to rewrite pages into the app, or when sub-paths of the app are exposed
+at other URIs than that which the app is 'normally' based at with C), the resolution of
+C<< $c->request->base >> will be incorrect.
+
+=back
=item *