__PACKAGE__->mk_classdata($_)
for qw/components arguments dispatcher engine log dispatcher_class
engine_loader context_class request_class response_class stats_class
- setup_finished _psgi_app/;
+ setup_finished _psgi_app loading_psgi_file/;
__PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
__PACKAGE__->request_class('Catalyst::Request');
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.89002';
+our $VERSION = '5.90004';
sub import {
my ( $class, @arguments ) = @_;
use Catalyst qw/-Debug/; # include plugins here as well
### In lib/MyApp/Controller/Root.pm (autocreated)
- sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc.
+ sub foo : Chained('/') Args() { # called for /foo, /foo/1, /foo/1/2, etc.
my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2
$c->stash->{template} = 'foo.tt'; # set the template
# lookup something from db -- stash vars are passed to TT
[% END %]
# called for /bar/of/soap, /bar/of/soap/10, etc.
- sub bar : Path('/bar/of/soap') { ... }
-
- # called for all actions, from the top-most controller downwards
- sub auto : Private {
- my ( $self, $c ) = @_;
- if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication
- $c->res->redirect( '/login' ); # require login
- return 0; # abort request and go immediately to end()
- }
- return 1; # success; carry on to next action
- }
+ sub bar : Chained('/') PathPart('/bar/of/soap') Args() { ... }
# called after all actions are finished
- sub end : Private {
+ sub end : Action {
my ( $self, $c ) = @_;
if ( scalar @{ $c->error } ) { ... } # handle errors
return if $c->res->body; # already have a response
$c->forward( 'MyApp::View::TT' ); # render template
}
- ### in MyApp/Controller/Foo.pm
- # called for /foo/bar
- sub bar : Local { ... }
-
- # called for /blargle
- sub blargle : Global { ... }
-
- # an index action matches /foo, but not /foo/1, etc.
- sub index : Private { ... }
-
- ### in MyApp/Controller/Foo/Bar.pm
- # called for /foo/bar/baz
- sub baz : Local { ... }
-
- # first Root auto is called, then Foo auto, then this
- sub auto : Private { ... }
-
- # powerful regular expression paths are also possible
- sub details : Regex('^product/(\w+)/details$') {
- my ( $self, $c ) = @_;
- # extract the (\w+) from the URI
- my $product = $c->req->captures->[0];
- }
-
See L<Catalyst::Manual::Intro> for additional information.
=head1 DESCRIPTION
return $c->_filter_component( $comp, @args ) if $comp;
}
+ return
+ if $c->config->{disable_component_resolution_regex_fallback};
+
# This is here so $c->comp( '::M::' ) works
my $query = ref $name ? $name : qr{$name}i;
$class->setup_plugins( delete $flags->{plugins} );
$class->setup_dispatcher( delete $flags->{dispatcher} );
if (my $engine = delete $flags->{engine}) {
- $class->log->warn("Specifying the engine in ->setup is no longer supported, XXX FIXME");
+ $class->log->warn("Specifying the engine in ->setup is no longer supported, see Catalyst::Upgrading");
}
$class->setup_engine();
$class->setup_stats( delete $flags->{stats} );
A hook to attach modifiers to. This method does not do anything except set the
C<setup_finished> accessor.
-Applying method modifiers to the C<setup> method doesn't work, because of quirky thingsdone for plugin setup.
+Applying method modifiers to the C<setup> method doesn't work, because of quirky things done for plugin setup.
Example:
$error = qq/Caught exception in $class->$name "$error"/;
}
$c->error($error);
- $c->state(0);
}
+ $c->state(0);
}
return $c->state;
}
if ( !$response->has_body ) {
# Add a default body if none is already present
- $response->body(
- qq{<html><body><p>This item has moved <a href="$location">here</a>.</p></body></html>}
- );
+ $response->body(<<"EOF");
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Moved</title>
+ </head>
+ <body>
+ <p>This item has moved <a href="$location">here</a>.</p>
+ </body>
+</html>
+EOF
+ $response->content_type('text/html; charset=utf-8');
}
}
return $status;
}
-=head2 $c->prepare( @arguments )
+=head2 $class->prepare( @arguments )
Creates a Catalyst context from an engine-specific request (Apache, CGI,
etc.).
=cut
-sub run { my $c = shift; return $c->engine->run( $c, $c->_finalized_psgi_app, @_ ) }
+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 )
=cut
sub engine_class {
- my $class = shift;
- $class->engine_loader->catalyst_engine_class(@_);
+ my ($class, $requested_engine) = @_;
+
+ if (!$class->engine_loader || $requested_engine) {
+ $class->engine_loader(
+ Catalyst::EngineLoader->new({
+ application_name => $class,
+ (defined $requested_engine
+ ? (catalyst_engine_class => $requested_engine) : ()),
+ }),
+ );
+ }
+
+ $class->engine_loader->catalyst_engine_class;
}
sub setup_engine {
my ($class, $requested_engine) = @_;
- $class->engine_loader(
- Catalyst::EngineLoader->new({
- application_name => $class,
- (defined $requested_engine
- ? (requested_engine => $requested_engine) : ()),
- }),
- );
+ my $engine = do {
+ my $loader = $class->engine_loader;
+
+ if (!$loader || $requested_engine) {
+ $loader = Catalyst::EngineLoader->new({
+ application_name => $class,
+ (defined $requested_engine
+ ? (requested_engine => $requested_engine) : ()),
+ }),
+
+ $class->engine_loader($loader);
+ }
+
+ $loader->catalyst_engine_class;
+ };
+
+ # Don't really setup_engine -- see _setup_psgi_app for explanation.
+ return if $class->loading_psgi_file;
- my $engine = $class->engine_class;
Class::MOP::load_class($engine);
if ($ENV{MOD_PERL}) {
my $apache = $class->engine_loader->auto;
- # FIXME - Immutable
- $class->meta->add_method(handler => sub {
+
+ my $meta = find_meta($class);
+ my $was_immutable = $meta->is_immutable;
+ my %immutable_options = $meta->immutable_options;
+ $meta->make_mutable if $was_immutable;
+
+ $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;
}
$class->engine( $engine->new );
);
next unless -e $psgi_file;
+
+ # 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.
+
+ $app->loading_psgi_file(1);
my $psgi_app = Plack::Util::load_psgi($psgi_file);
+ $app->loading_psgi_file(0);
return $psgi_app
unless $app->engine_loader->needs_psgi_engine_compat_hack;
- # load_psgi ran a .psgi file doing ->setup_engine('PSGI'). That's what
- # .psgi files generated by the old Engine::PSGI do. Those return an app
- # coderef calling into MyApp->run, which doesn't work anymore, so we're
- # just ignoring it and use the wrapped legacy psgi app
warn <<"EOW";
Found a legacy Catalyst::Engine::PSGI .psgi file at ${psgi_file}.
EOW
}
- return $app->_wrapped_legacy_psgi_app($app->psgi_app);
+ return $app->apply_default_middlewares($app->psgi_app);
}
-# Note - this is for back compatibility. Catalyst should not know or care about
-# how it's deployed. The recommended way of configuring this is now to
-# use the ReverseProxy middleware yourself if you want it in a .psgi
-# file.
-sub _wrapped_legacy_psgi_app {
+=head2 $c->apply_default_middlewares
+
+Adds the following L<Plack> middlewares to your application, since they are
+useful and commonly needed:
+
+L<Plack::Middleware::ReverseProxy>, (conditionally added based on the status
+of your $ENV{REMOTE_ADDR}, and can be forced on with C<using_frontend_proxy>
+or forced off with C<ignore_frontend_proxy>), L<Plack::Middleware::LighttpdScriptNameFix>
+(if you are using Lighttpd), L<Plack::Middleware::IIS6ScriptNameFix> (always
+applied since this middleware is smart enough to conditionally apply itself).
+
+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}
+
+=cut
+
+
+sub apply_default_middlewares {
my ($app, $psgi_app) = @_;
$psgi_app = Plack::Middleware::Conditional->wrap(
},
);
- my $server_matches = sub {
- my ($re) = @_;
- return sub {
- my ($env) = @_;
- my $server = $env->{SERVER_SOFTWARE};
- return unless $server;
- return $server =~ $re ? 1 : 0;
- };
- };
-
# 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);
- $psgi_app = Plack::Middleware::Conditional->wrap(
- $psgi_app,
- condition => $server_matches->(qr/^nginx/),
- builder => sub {
- my ($to_wrap) = @_;
- return sub {
- my ($env) = @_;
- my $script_name = $env->{SCRIPT_NAME};
- $env->{PATH_INFO} =~ s/^$script_name//g;
- return $to_wrap->($env);
- };
- },
- );
-
# 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
my $class = ref $proto || $proto;
Class::MOP::load_class( $plugin );
- $class->log->warn( "$plugin inherits from 'Catalyst::Component' - this is decated and will not work in 5.81" )
+ $class->log->warn( "$plugin inherits from 'Catalyst::Component' - this is deprecated and will not work in 5.81" )
if $plugin->isa( 'Catalyst::Component' );
$proto->_plugins->{$plugin} = 1;
unless ($instant) {
=item *
C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
-variable should be used for determining the request path. See L<Catalyst::Engine::CGI/PATH DECODING>
-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<PATH_INFO> and C<SCRIPT_NAME> environment variables.
+The allows the application to behave correctly when C<mod_rewrite> 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<always> 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<REQUEST_URI> and C<SCRIPT_NAME> environment variables. As C<REQUEST_URI> is never
+decoded, this means that applications using this mode can correctly handle URIs including the %2F character
+(i.e. with C<AllowEncodedSlashes> set to C<On> 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<mod_rewrite>), the resolution of
+C<< $c->request->base >> will be incorrect.
+
+=back
=item *