use Plack::Middleware::IIS6ScriptNameFix;
use Plack::Middleware::IIS7KeepAliveFix;
use Plack::Middleware::LighttpdScriptNameFix;
+use Plack::Middleware::ContentLength;
+use Plack::Middleware::Head;
+use Plack::Middleware::HTTPExceptions;
+use Plack::Middleware::FixMissingBodyInRedirect;
+use Plack::Middleware::MethodOverride;
+use Plack::Middleware::RemoveRedundantBody;
use Plack::Util;
use Class::Load 'load_class';
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90049_004';
+our $VERSION = '5.90059_005';
sub import {
my ( $class, @arguments ) = @_;
$class->setup_log( delete $flags->{log} );
$class->setup_plugins( delete $flags->{plugins} );
+
+ # Call plugins setup, this is stupid and evil.
+ # Also screws C3 badly on 5.10, hack to avoid.
+ {
+ no warnings qw/redefine/;
+ local *setup = sub { };
+ $class->setup unless $Catalyst::__AM_RESTARTING;
+ }
+
$class->setup_middleware();
$class->setup_data_handlers();
$class->setup_dispatcher( delete $flags->{dispatcher} );
EOF
}
+ # Initialize our data structure
+ $class->components( {} );
+
+ $class->setup_components;
+
if ( $class->debug ) {
my @plugins = map { "$_ " . ( $_->VERSION || '' ) } $class->registered_plugins;
? $class->log->debug(qq/Found home "$home"/)
: $class->log->debug(qq/Home "$home" doesn't exist/)
: $class->log->debug(q/Couldn't find home/);
- }
- # Call plugins setup, this is stupid and evil.
- # Also screws C3 badly on 5.10, hack to avoid.
- {
- no warnings qw/redefine/;
- local *setup = sub { };
- $class->setup unless $Catalyst::__AM_RESTARTING;
- }
-
- # Initialize our data structure
- $class->components( {} );
-
- $class->setup_components;
-
- if ( $class->debug ) {
my $column_width = Catalyst::Utils::term_width() - 8 - 9;
my $t = Text::SimpleTable->new( [ $column_width, 'Class' ], [ 8, 'Type' ] );
for my $comp ( sort keys %{ $class->components } ) {
# Should be the last thing we do so that user things hooking
# setup_finalize can log..
$class->log->_flush() if $class->log->can('_flush');
- return 1; # Explicit return true as people have __PACKAGE__->setup as the last thing in their class. HATE.
+ return $class || 1; # Just in case someone named their Application 0...
}
=head2 $app->setup_finalize
# Support skipping finalize for psgix.io style 'jailbreak'. Used to support
# stuff like cometd and websockets
- if($c->request->has_io_fh) {
+ if($c->request->_has_io_fh) {
$c->log_response;
return;
}
$c->finalize_headers unless $c->response->finalized_headers;
- # HEAD request
- if ( $c->request->method eq 'HEAD' ) {
- $c->response->body('');
- }
-
$c->finalize_body;
}
=head2 $c->finalize_error
-Finalizes error.
+Finalizes error. If there is only one error in L</error> and it is an object that
+does C<as_psgi> or C<code> we rethrow the error and presume it caught by middleware
+up the ladder. Otherwise we return the debugging error page (in debug mode) or we
+return the default error page (production mode).
=cut
-sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
+sub finalize_error {
+ my $c = shift;
+ if($#{$c->error} > 0) {
+ $c->engine->finalize_error( $c, @_ );
+ } else {
+ my ($error) = @{$c->error};
+ if(
+ blessed $error &&
+ ($error->can('as_psgi') || $error->can('code'))
+ ) {
+ # In the case where the error 'knows what it wants', becauses its PSGI
+ # aware, just rethow and let middleware catch it
+ $error->can('rethrow') ? $error->rethrow : croak $error;
+ croak $error;
+ } else {
+ $c->engine->finalize_error( $c, @_ )
+ }
+ }
+}
=head2 $c->finalize_headers
if ( my $location = $response->redirect ) {
$c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
$response->header( Location => $location );
-
- if ( !$response->has_body ) {
- # Add a default body if none is already present
- my $encoded_location = encode_entities($location);
- $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="$encoded_location">here</a>.</p>
- </body>
-</html>
-EOF
- $response->content_type('text/html; charset=utf-8');
- }
- }
-
- # Content-Length
- if ( defined $response->body && length $response->body && !$response->content_length ) {
-
- # get the length from a filehandle
- if ( blessed( $response->body ) && $response->body->can('read') || ref( $response->body ) eq 'GLOB' )
- {
- my $size = -s $response->body;
- if ( $size ) {
- $response->content_length( $size );
- }
- else {
- $c->log->warn('Serving filehandle without a content-length');
- }
- }
- else {
- # everything should be bytes at this point, but just in case
- $response->content_length( length( $response->body ) );
- }
}
- # Errors
- if ( $response->status =~ /^(1\d\d|[23]04)$/ ) {
- $response->headers->remove_header("Content-Length");
- $response->body('');
- }
+ # Remove incorrectly added body and content related meta data when returning
+ # an information response, or a response the is required to not include a body
$c->finalize_cookies;
my $c = $class->prepare(@arguments);
$c->dispatch;
$status = $c->finalize;
- }
- catch {
+ } catch {
chomp(my $error = $_);
$class->log->error(qq/Caught exception in engine "$error"/);
+ #rethow if this can be handled by middleware
+ if(blessed $error && ($error->can('as_psgi') || $error->can('code'))) {
+ $error->can('rethrow') ? $error->rethrow : croak $error;
+ }
};
$COUNT++;
to use it to enable L<Plack::Middleware>
This method is automatically called during 'setup' of your application, so
-you really don't need to invoke it.
+you really don't need to invoke it. However you may do so if you find the idea
+of loading middleware via configuration weird :). For example:
+
+ package MyApp;
+
+ use Catalyst;
+
+ __PACKAGE__->setup_middleware('Head');
+ __PACKAGE__->setup;
When we read middleware definitions from configuration, we reverse the list
which sounds odd but is likely how you expect it to work if you have prior
sub registered_middlewares {
my $class = shift;
if(my $middleware = $class->_psgi_middleware) {
- return @$middleware;
+ return (
+ Plack::Middleware::HTTPExceptions->new,
+ Plack::Middleware::RemoveRedundantBody->new,
+ Plack::Middleware::FixMissingBodyInRedirect->new,
+ Plack::Middleware::ContentLength->new,
+ Plack::Middleware::MethodOverride->new,
+ Plack::Middleware::Head->new,
+ @$middleware);
} else {
die "You cannot call ->registered_middlewares until middleware has been setup";
}
}
sub setup_middleware {
- my ($class, @middleware_definitions) = @_;
- push @middleware_definitions, reverse(
- @{$class->config->{'psgi_middleware'}||[]});
+ my $class = shift;
+ my @middleware_definitions = @_ ?
+ @_ : reverse(@{$class->config->{'psgi_middleware'}||[]});
my @middleware = ();
while(my $next = shift(@middleware_definitions)) {
}
}
- $class->_psgi_middleware(\@middleware);
+ my @existing = @{$class->_psgi_middleware || []};
+ $class->_psgi_middleware([@middleware,@existing,]);
}
=head2 registered_data_handlers
=back
+=head1 EXCEPTIONS
+
+Generally when you throw an exception inside an Action (or somewhere in
+your stack, such as in a model that an Action is calling) that exception
+is caught by Catalyst and unless you either catch it yourself (via eval
+or something like L<Try::Tiny> or by reviewing the L</error> stack, it
+will eventually reach L</finalize_errors> and return either the debugging
+error stack page, or the default error page. However, if your exception
+can be caught by L<Plack::Middleware::HTTPExceptions>, L<Catalyst> will
+instead rethrow it so that it can be handled by that middleware (which
+is part of the default middleware). For example this would allow
+
+ use HTTP::Throwable::Factory 'http_throw';
+
+ sub throws_exception :Local {
+ my ($self, $c) = @_;
+
+ http_throw(SeeOther => { location =>
+ $c->uri_for($self->action_for('redirect')) });
+
+ }
+
=head1 INTERNAL ACTIONS
Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,
Where C<@middleware> is one or more of the following, applied in the REVERSE of
the order listed (to make it function similarly to L<Plack::Builder>:
+
+Alternatively, you may also define middleware by calling the L</setup_middleware>
+package method:
+
+ package MyApp::Web;
+
+ use Catalyst;
+
+ __PACKAGE__->setup_middleware( \@middleware_definitions);
+ __PACKAGE__->setup;
+
+In the case where you do both (use 'setup_middleware' and configuration) the
+package call to setup_middleware will be applied earlier (in other words its
+middleware will wrap closer to the application). Keep this in mind since in
+some cases the order of middleware is important.
+
+The two approaches are not exclusive.
=over 4
dd070: Dhaval Dhanani <dhaval070@gmail.com>
+Upasana <me@upasana.me>
+
=head1 COPYRIGHT
-Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.
+Copyright (c) 2005-2014, the above named PROJECT FOUNDER and CONTRIBUTORS.
=head1 LICENSE