more release docs updates, and fixed some config options;
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 3ef4bc8..1b3af91 100644 (file)
@@ -129,7 +129,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC);
 
 # Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90079_004';
+our $VERSION = '5.90079_005';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 sub import {
@@ -533,6 +533,9 @@ Add a new error.
 
     $c->error('Something bad happened');
 
+Calling this will always return an arrayref (if there are no errors it
+will be an empty arrayref.
+
 =cut
 
 sub error {
@@ -577,6 +580,29 @@ Returns true if you have errors
 
 sub has_errors { scalar(@{shift->error}) ? 1:0 }
 
+=head2 $c->last_error
+
+Returns the most recent error in the stack (the one most recently added...)
+or nothing if there are no errors.
+
+=cut
+
+sub last_error { my ($err, @errs) = @{shift->error}; return $err }
+
+=head2 shift_errors
+
+shifts the most recently added error off the error stack and returns if.  Returns
+nothing if there are nomore errors.
+
+=cut
+
+sub shift_errors {
+    my ($self) = @_;
+    my ($err, @errors) = @{$self->error};
+    $self->{error} = \@errors;
+    return $err;
+}
+
 sub _comp_search_prefixes {
     my $c = shift;
     return map $c->components->{ $_ }, $c->_comp_names_search_prefixes(@_);
@@ -1841,15 +1867,7 @@ sub execute {
 
     if ( my $error = $@ ) {
         #rethow if this can be handled by middleware
-        if(
-          blessed $error && (
-            $error->can('as_psgi') ||
-            (
-              $error->can('code') &&
-              $error->code =~m/^[1-5][0-9][0-9]$/
-            )
-          )
-        ) {
+        if ( $c->_handle_http_exception($error) ) {
             foreach my $err (@{$c->error}) {
                 $c->log->error($err);
             }
@@ -1962,7 +1980,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) {
       $c->log_response;
       return;
@@ -2030,10 +2048,7 @@ sub finalize_error {
         $c->engine->finalize_error( $c, @_ );
     } else {
         my ($error) = @{$c->error};
-        if(
-          blessed $error &&
-          ($error->can('as_psgi') || $error->can('code'))
-        ) {
+        if ( $c->_handle_http_exception($error) ) {
             # 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;
@@ -2184,15 +2199,7 @@ sub handle_request {
         $status = $c->finalize;
     } catch {
         #rethow if this can be handled by middleware
-        if(
-          blessed($_) && (
-            $_->can('as_psgi') ||
-            (
-              $_->can('code') &&
-              $_->code =~m/^[1-5][0-9][0-9]$/
-            )
-          )
-        ) {
+        if ( $class->_handle_http_exception($_) ) {
             $_->can('rethrow') ? $_->rethrow : croak $_;
         }
         chomp(my $error = $_);
@@ -2999,15 +3006,30 @@ EOW
 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).
+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).
+
+We will also automatically add L<Plack::Middleware::ReverseProxy> if we notice
+that your HTTP $env variable C<REMOTE_ADDR> is '127.0.0.1'.  This is usually
+an indication that your server is running behind a proxy frontend.  However in
+2014 this is often not the case.  We preserve this code for backwards compatibility
+however I B<highly> recommend that if you are running the server behind a front
+end proxy that you clearly indicate so with the C<using_frontend_proxy> configuration
+setting to true for your environment configurations that run behind a proxy.  This
+way if you change your front end proxy address someday your code would inexplicably
+stop working as expected.
 
 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}
+$ENV{SCRIPT_NAME}.
+
+Please B<NOTE> that if you do use C<using_frontend_proxy> the middleware is now
+adding via C<registered_middleware> rather than this method.
+
+If you are using Lighttp or IIS6 you may wish to apply these middlewares.  In
+general this is no longer a common case but we have this here for backward
+compatibility.
 
 =cut
 
@@ -3015,16 +3037,21 @@ $ENV{SCRIPT_NAME}
 sub apply_default_middlewares {
     my ($app, $psgi_app) = @_;
 
-    $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};
-        },
-    );
+    # Don't add this conditional IF we are explicitly saying we want the
+    # frontend proxy support.  We don't need it here since if that is the
+    # case it will be always loaded in the default_middleware.
+
+    unless($app->config->{using_frontend_proxy}) {
+      $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';
+          },
+      );
+    }
 
     # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME
     # http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html
@@ -3062,11 +3089,24 @@ sub apply_default_middlewares {
 =head2 App->to_app
 
 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.
+C<$c>. This is the bare application created without the C<apply_default_middlewares>
+method called.  We do however apply C<registered_middleware> since those are
+integral to how L<Catalyst> functions.  Also, unlike starting your application
+with a generated server script (via L<Catalyst::Devel> and C<catalyst.pl>) we do
+not attempt to return a valid L<PSGI> application using any existing C<${myapp}.psgi>
+scripts in your $HOME directory.
+
+B<NOTE> C<apply_default_middlewares> was orginally created when the first PSGI
+port was done for v5.90000.  These are middlewares that are added to achieve
+backward compatibility with older applications.  If you start your application
+using one of the supplied server scripts (generated with L<Catalyst::Devel> and
+the project skeleton script C<catalyst.pl>) we apply C<apply_default_middlewares>
+automatically.  This was done so that pre and post PSGI port applications would
+work the same way.
 
 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.
+reference of your Catalyst application for use in a custom F<.psgi> or in your
+own created server modules.
 
 =cut
 
@@ -3102,7 +3142,7 @@ sub setup_home {
 
 =head2 $c->setup_encoding
 
-Sets up the input/output encoding.  See L<ENCODING>
+Sets up the input/output encoding. See L<ENCODING>
 
 =cut
 
@@ -3344,7 +3384,69 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
             $class => @roles
         ) if @roles;
     }
-}    
+}
+
+=head2 default_middleware
+
+Returns a list of instantiated PSGI middleware objects which is the default
+middleware that is active for this application (taking any configuration
+options into account, excluding your custom added middleware via the C<psgi_middleware>
+configuration option).  You can override this method if you wish to change
+the default middleware (although do so at risk since some middleware is vital
+to application function.)
+
+The current default middleware list is:
+
+      Catalyst::Middleware::Stash
+      Plack::Middleware::HTTPExceptions
+      Plack::Middleware::RemoveRedundantBody
+      Plack::Middleware::FixMissingBodyInRedirect
+      Plack::Middleware::ContentLength
+      Plack::Middleware::MethodOverride
+      Plack::Middleware::Head
+
+If the configuration setting C<using_frontend_proxy> is true we add:
+
+      Plack::Middleware::ReverseProxy
+
+If the configuration setting C<using_frontend_proxy_path> is true we add:
+
+      Plack::Middleware::ReverseProxyPath
+
+But B<NOTE> that L<Plack::Middleware::ReverseProxyPath> is not a dependency of the
+L<Catalyst> distribution so if you want to use this option you should add it to
+your project distribution file.
+
+These middlewares will be added at L</setup_middleware> during the
+L</setup> phase of application startup.
+
+=cut
+
+sub default_middleware {
+    my $class = shift;
+    my @mw = (
+      Catalyst::Middleware::Stash->new,
+      Plack::Middleware::HTTPExceptions->new,
+      Plack::Middleware::RemoveRedundantBody->new,
+      Plack::Middleware::FixMissingBodyInRedirect->new,
+      Plack::Middleware::ContentLength->new,
+      Plack::Middleware::MethodOverride->new,
+      Plack::Middleware::Head->new);
+
+    if($class->config->{using_frontend_proxy}) {
+        push @mw, Plack::Middleware::ReverseProxy->new;
+    }
+
+    if($class->config->{using_frontend_proxy_path}) {
+        if(Class::Load::try_load_class('Plack::Middleware::ReverseProxyPath')) {
+            push @mw, Plack::Middleware::ReverseProxyPath->new;
+        } else {
+          $class->log->error("Cannot use configuration 'using_frontend_proxy_path' because 'Plack::Middleware::ReverseProxyPath' is not installed");
+        }
+    }
+
+    return @mw;
+}
 
 =head2 registered_middlewares
 
@@ -3387,15 +3489,13 @@ up.
 sub registered_middlewares {
     my $class = shift;
     if(my $middleware = $class->_psgi_middleware) {
-        return (
-          Catalyst::Middleware::Stash->new,
-          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);
+        my @mw = ($class->default_middleware, @$middleware);
+
+        if($class->config->{using_frontend_proxy}) {
+          push @mw, Plack::Middleware::ReverseProxy->new;
+        }
+
+        return @mw;
     } else {
         die "You cannot call ->registered_middlewares until middleware has been setup";
     }
@@ -3403,8 +3503,17 @@ sub registered_middlewares {
 
 sub setup_middleware {
     my $class = shift;
-    my @middleware_definitions = @_ ? 
-      reverse(@_) : reverse(@{$class->config->{'psgi_middleware'}||[]});
+    my @middleware_definitions;
+
+    # If someone calls this method you can add middleware with args.  However if its
+    # called without an arg we need to setup the configuration middleware.
+    if(@_) {
+      @middleware_definitions = reverse(@_);
+    } else {
+      @middleware_definitions = reverse(@{$class->config->{'psgi_middleware'}||[]})
+        unless $class->config->{__configured_from_psgi_middleware};
+      $class->config->{__configured_from_psgi_middleware} = 1; # Only do this once, just in case some people call setup over and over...
+    }
 
     my @middleware = ();
     while(my $next = shift(@middleware_definitions)) {
@@ -3495,12 +3604,34 @@ sub default_data_handlers {
             ->can('build_cgi_struct')->($params);
       },
       'application/json' => sub {
-          Class::Load::load_first_existing_class('JSON::MaybeXS', 'JSON')
-            ->can('decode_json')->(do { local $/; $_->getline });
-      },
+          my ($fh, $req) = @_;
+          my $parser = Class::Load::load_first_existing_class('JSON::MaybeXS', 'JSON');
+          my $slurped;
+          return eval { 
+            local $/;
+            $slurped = $fh->getline;
+            $parser->can("decode_json")->($slurped);
+          } || Catalyst::Exception->throw(sprintf "Error Parsing POST '%s', Error: %s", (defined($slurped) ? $slurped : 'undef') ,$@);
+        },
     };
 }
 
+sub _handle_http_exception {
+    my ( $self, $error ) = @_;
+    if (
+           !$self->config->{always_catch_http_exceptions}
+        && blessed $error
+        && (
+            $error->can('as_psgi')
+            || (   $error->can('code')
+                && $error->code =~ m/^[1-5][0-9][0-9]$/ )
+        )
+      )
+    {
+        return 1;
+    }
+}
+
 =head2 $c->stack
 
 Returns an arrayref of the internal execution stack (actions that are
@@ -3567,6 +3698,13 @@ There are a number of 'base' config variables which can be set:
 
 =item *
 
+C<always_catch_http_exceptions> - As of version 5.90060 Catalyst
+rethrows errors conforming to the interface described by
+L<Plack::Middleware::HTTPExceptions> and lets the middleware deal with it.
+Set true to get the deprecated behaviour and have Catalyst catch HTTP exceptions.
+
+=item *
+
 C<default_model> - The default model picked if you say C<< $c->model >>. See L<< /$c->model($name) >>.
 
 =item *
@@ -3622,7 +3760,7 @@ to be shown in hit debug tables in the test server.
 =item *
 
 C<use_request_uri_for_path> - Controls if the C<REQUEST_URI> or C<PATH_INFO> environment
-variable should be used for determining the request path. 
+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,
@@ -3661,7 +3799,7 @@ is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html director
 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 
+=back
 
 =item *
 
@@ -3669,6 +3807,15 @@ C<using_frontend_proxy> - See L</PROXY SUPPORT>.
 
 =item *
 
+C<using_frontend_proxy_path> - Enabled L<Plack::Middleware::ReverseProxyPath> on your application (if
+installed, otherwise log an error).  This is useful if your application is not running on the
+'root' (or /) of your host server.  B<NOTE> if you use this feature you should add the required
+middleware to your project dependency list since its not automatically a dependency of L<Catalyst>.
+This has been done since not all people need this feature and we wish to restrict the growth of
+L<Catalyst> dependencies.
+
+=item *
+
 C<encoding> - See L</ENCODING>
 
 This now defaults to 'UTF-8'.  You my turn it off by setting this configuration
@@ -3682,7 +3829,7 @@ When there is an error in an action chain, the default behavior is to continue
 processing the remaining actions and then catch the error upon chain end.  This
 can lead to running actions when the application is in an unexpected state.  If
 you have this issue, setting this config value to true will promptly exit a
-chain when there is an error raised in any action (thus terminating the chain 
+chain when there is an error raised in any action (thus terminating the chain
 early.)
 
 use like:
@@ -3728,7 +3875,7 @@ 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 
+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
@@ -3738,7 +3885,7 @@ is part of the default middleware).  For example this would allow
     sub throws_exception :Local {
       my ($self, $c) = @_;
 
-      http_throw(SeeOther => { location => 
+      http_throw(SeeOther => { location =>
         $c->uri_for($self->action_for('redirect')) });
 
     }
@@ -4028,9 +4175,8 @@ matches the following regular expression:
 
     $content_type =~ /^text|xml$|javascript$/
 
-Encoding is set on the application, but it is copied to the response object
-so you can override encoding rules per request (See L<Catalyst::Response>
-for more information).
+Encoding is set on the application, but it is copied to the context object
+so that you can override it on a request basis.
 
 Be default we don't automatically encode 'application/json' since the most
 popular JSON encoders (such as L<JSON::MaybeXS> which is the library that
@@ -4162,6 +4308,8 @@ Chisel Wright C<pause@herlpacker.co.uk>
 
 Danijel Milicevic C<me@danijel.de>
 
+davewood: David Schmidt <davewood@cpan.org>
+
 David Kamholz E<lt>dkamholz@cpan.orgE<gt>
 
 David Naughton, C<naughton@umn.edu>