upping versions and prep release
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index ae07a90..8173fb0 100644 (file)
@@ -41,6 +41,12 @@ use Plack::Middleware::ReverseProxy;
 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';
 
@@ -120,7 +126,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.90049_004';
+our $VERSION = '5.90060';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -1126,6 +1132,15 @@ sub setup {
 
     $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} );
@@ -1159,6 +1174,11 @@ You are running an old script!
 EOF
     }
 
+    # Initialize our data structure
+    $class->components( {} );
+
+    $class->setup_components;
+
     if ( $class->debug ) {
         my @plugins = map { "$_  " . ( $_->VERSION || '' ) } $class->registered_plugins;
 
@@ -1202,22 +1222,7 @@ EOF
           ? $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 } ) {
@@ -1249,7 +1254,7 @@ EOF
     # 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
@@ -1837,7 +1842,7 @@ sub 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;
     }
@@ -1858,11 +1863,6 @@ sub finalize {
 
         $c->finalize_headers unless $c->response->finalized_headers;
 
-        # HEAD request
-        if ( $c->request->method eq 'HEAD' ) {
-            $c->response->body('');
-        }
-
         $c->finalize_body;
     }
 
@@ -1896,11 +1896,32 @@ sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
 
 =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
 
@@ -1920,50 +1941,10 @@ sub 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;
 
@@ -2032,10 +2013,13 @@ sub handle_request {
         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++;
@@ -3103,7 +3087,15 @@ See under L</CONFIGURATION> information regarding C<psgi_middleware> and how
 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
@@ -3115,16 +3107,23 @@ L<Catalyst::Plugin::EnableMiddleware> (which is now considered deprecated)
 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)) {
@@ -3146,7 +3145,8 @@ sub setup_middleware {
         }
     }
 
-    $class->_psgi_middleware(\@middleware);
+    my @existing = @{$class->_psgi_middleware || []};
+    $class->_psgi_middleware([@middleware,@existing,]);
 }
 
 =head2 registered_data_handlers
@@ -3210,7 +3210,7 @@ sub default_data_handlers {
           my ($fh, $req) = @_;
           my $params = $req->_use_hash_multivalue ? $req->body_parameters->mixed : $req->body_parameters;
           Class::Load::load_first_existing_class('CGI::Struct::XS', 'CGI::Struct')
-            ->('build_cgi_struct')->($params);
+            ->can('build_cgi_struct')->($params);
       },
       'application/json' => sub {
           Class::Load::load_first_existing_class('JSON::MaybeXS', 'JSON')
@@ -3436,6 +3436,28 @@ C<data_handlers> - See L<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>,
@@ -3604,6 +3626,23 @@ So the general form is:
 
 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
  
@@ -3918,9 +3957,11 @@ rainboxx: Matthias Dietrich, C<perl@rainboxx.de>
 
 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