X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FEngine.pm;h=691bd675e00ba5701cffa665f0fed7b9db0ff5b5;hb=a2f2cde95194a17fe2401ae58c92b5494bac599f;hp=e25b2054c4d27ba34eaee2cb8a08f55b1bd18937;hpb=c539a1c32cc974e531bea851e9f393800ed7a1c0;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst/Engine.pm b/lib/Catalyst/Engine.pm index e25b205..691bd67 100644 --- a/lib/Catalyst/Engine.pm +++ b/lib/Catalyst/Engine.pm @@ -10,9 +10,11 @@ use HTML::Entities; use HTTP::Headers; use Time::HiRes qw/gettimeofday tv_interval/; use Text::ASCIITable; +use Catalyst::Exception; use Catalyst::Request; use Catalyst::Request::Upload; use Catalyst::Response; +use Catalyst::Utils; require Module::Pluggable::Fast; @@ -20,7 +22,7 @@ require Module::Pluggable::Fast; $Data::Dumper::Terse = 1; __PACKAGE__->mk_classdata('components'); -__PACKAGE__->mk_accessors(qw/request response state/); +__PACKAGE__->mk_accessors(qw/counter depth request response state/); *comp = \&component; *req = \&request; @@ -30,8 +32,10 @@ __PACKAGE__->mk_accessors(qw/request response state/); *finalize_output = \&finalize_body; # For statistics -our $COUNT = 1; -our $START = time; +our $COUNT = 1; +our $START = time; +our $RECURSION = 1000; +our $DETACH = "catalyst_detach\n"; =head1 NAME @@ -80,20 +84,35 @@ Regex search for a component. =cut sub component { - my ( $c, $name ) = @_; + my $c = shift; - if ( my $component = $c->components->{$name} ) { - return $component; - } + if (@_) { - else { - for my $component ( keys %{ $c->components } ) { - return $c->components->{$component} if $component =~ /$name/i; + my $name = shift; + + if ( my $component = $c->components->{$name} ) { + return $component; + } + + else { + for my $component ( keys %{ $c->components } ) { + return $c->components->{$component} if $component =~ /$name/i; + } } } + return sort keys %{ $c->components }; } +=item $c->counter + +Returns a hashref containing coderefs and execution counts. +(Needed for deep recursion detection) + +=item $c->depth + +Returns the actual forward depth. + =item $c->error =item $c->error($error, ...) @@ -126,34 +145,53 @@ Errors are available via $c->error. sub execute { my ( $c, $class, $code ) = @_; - $class = $c->comp($class) || $class; + $class = $c->components->{$class} || $class; $c->state(0); my $callsub = ( caller(1) )[3]; + my $action = ''; + if ( $c->debug ) { + $action = $c->actions->{reverse}->{"$code"}; + $action = "/$action" unless $action =~ /\-\>/; + $c->counter->{"$code"}++; + + if ( $c->counter->{"$code"} > $RECURSION ) { + my $error = qq/Deep recursion detected in "$action"/; + $c->log->error($error); + $c->error($error); + $c->state(0); + return $c->state; + } + + $action = "-> $action" if $callsub =~ /forward$/; + } + + $c->{depth}++; eval { if ( $c->debug ) { - my $action = $c->actions->{reverse}->{"$code"}; - $action = "/$action" unless $action =~ /\-\>/; - $action = "-> $action" if $callsub =~ /forward$/; my ( $elapsed, @state ) = $c->benchmark( $code, $class, $c, @{ $c->req->args } ); push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ]; $c->state(@state); } - else { $c->state( &$code( $class, $c, @{ $c->req->args } ) ) } + else { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) } }; + $c->{depth}--; if ( my $error = $@ ) { - unless ( ref $error ) { - chomp $error; - $error = qq/Caught exception "$error"/; - } + if ( $error eq $DETACH ) { die $DETACH if $c->{depth} > 1 } + else { + unless ( ref $error ) { + chomp $error; + $error = qq/Caught exception "$error"/; + } - $c->log->error($error); - $c->error($error); - $c->state(0); + $c->log->error($error); + $c->error($error); + $c->state(0); + } } return $c->state; } @@ -179,13 +217,21 @@ sub finalize { $c->finalize_error; } - if ( !$c->response->body && $c->response->status !~ /^(1|3)\d\d$/ ) { + if ( !$c->response->body && $c->response->status == 200 ) { $c->finalize_error; } if ( $c->response->body && !$c->response->content_length ) { - use bytes; # play safe with a utf8 aware perl - $c->response->content_length( length $c->response->body ); + $c->response->content_length( bytes::length( $c->response->body ) ); + } + + if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) { + $c->response->headers->remove_header("Content-Length"); + $c->response->body(''); + } + + if ( $c->request->method eq 'HEAD' ) { + $c->response->body(''); } my $status = $c->finalize_headers; @@ -195,7 +241,7 @@ sub finalize { =item $c->finalize_output -alias to finalize_body +, see finalize_body =item $c->finalize_body @@ -339,14 +385,14 @@ Finalize headers. sub finalize_headers { } -=item $c->handler( $class, $engine ) +=item $c->handler( $class, @arguments ) Handles the request. =cut sub handler { - my ( $class, $engine ) = @_; + my ( $class, @arguments ) = @_; # Always expect worst case! my $status = -1; @@ -354,7 +400,7 @@ sub handler { my @stats = (); my $handler = sub { - my $c = $class->prepare($engine); + my $c = $class->prepare(@arguments); $c->{stats} = \@stats; $c->dispatch; return $c->finalize; @@ -364,7 +410,8 @@ sub handler { my $elapsed; ( $elapsed, $status ) = $class->benchmark($handler); $elapsed = sprintf '%f', $elapsed; - my $av = sprintf '%.3f', 1 / $elapsed; + my $av = sprintf '%.3f', + ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) ); my $t = Text::ASCIITable->new; $t->setCols( 'Action', 'Time' ); $t->setColWidth( 'Action', 64, 1 ); @@ -387,7 +434,7 @@ sub handler { return $status; } -=item $c->prepare($r) +=item $c->prepare(@arguments) Turns the engine-specific request( Apache, CGI ... ) into a Catalyst context . @@ -395,21 +442,29 @@ into a Catalyst context . =cut sub prepare { - my ( $class, $engine ) = @_; + my ( $class, @arguments ) = @_; my $c = bless { + counter => {}, + depth => 0, request => Catalyst::Request->new( { arguments => [], cookies => {}, headers => HTTP::Headers->new, parameters => {}, + secure => 0, snippets => [], uploads => {} } ), response => Catalyst::Response->new( - { cookies => {}, headers => HTTP::Headers->new, status => 200 } + { + body => '', + cookies => {}, + headers => HTTP::Headers->new( 'Content-Length' => 0 ), + status => 200 + } ), stash => {}, state => 0 @@ -424,19 +479,18 @@ sub prepare { $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION ); } - $c->prepare_request($engine); - $c->prepare_path; + $c->prepare_request(@arguments); + $c->prepare_connection; $c->prepare_headers; $c->prepare_cookies; - $c->prepare_connection; + $c->prepare_path; $c->prepare_action; - my $method = $c->req->method || ''; - my $path = $c->req->path || ''; - my $hostname = $c->req->hostname || ''; - my $address = $c->req->address || ''; + my $method = $c->req->method || ''; + my $path = $c->req->path || ''; + my $address = $c->req->address || ''; - $c->log->debug(qq/"$method" request for "$path" from $hostname($address)/) + $c->log->debug(qq/"$method" request for "$path" from $address/) if $c->debug; if ( $c->request->method eq 'POST' and $c->request->content_length ) { @@ -462,8 +516,9 @@ sub prepare { $t->setCols( 'Key', 'Value' ); $t->setColWidth( 'Key', 37, 1 ); $t->setColWidth( 'Value', 36, 1 ); - for my $key ( keys %{ $c->req->params } ) { - my $value = $c->req->params->{$key} || ''; + for my $key ( sort keys %{ $c->req->params } ) { + my $param = $c->req->params->{$key}; + my $value = defined($param) ? $param : ''; $t->addRow( $key, $value ); } $c->log->debug( 'Parameters are', $t->draw ); @@ -626,7 +681,28 @@ Setup. sub setup { my $self = shift; + + # Initialize our data structure + $self->components( {} ); + $self->setup_components; + + if ( $self->debug ) { + my $t = Text::ASCIITable->new; + $t->setOptions( 'hide_HeadRow', 1 ); + $t->setOptions( 'hide_HeadLine', 1 ); + $t->setCols('Class'); + $t->setColWidth( 'Class', 75, 1 ); + $t->addRow($_) for sort keys %{ $self->components }; + $self->log->debug( 'Loaded components', $t->draw ) + if ( @{ $t->{tbl_rows} } ); + } + + # Add our self to components, since we are also a component + $self->components->{$self} = $self; + + $self->setup_actions; + if ( $self->debug ) { my $name = $self->config->{name} || 'Application'; $self->log->info("$name powered by Catalyst $Catalyst::VERSION"); @@ -642,38 +718,56 @@ Setup components. sub setup_components { my $self = shift; - # Components - my $class = ref $self || $self; - eval <<""; - package $class; - import Module::Pluggable::Fast - name => '_components', - search => [ - '$class\::Controller', '$class\::C', - '$class\::Model', '$class\::M', - '$class\::View', '$class\::V' - ]; + my $callback = sub { + my ( $component, $context ) = @_; + + unless ( $component->isa('Catalyst::Base') ) { + return $component; + } + + my $suffix = Catalyst::Utils::class2classsuffix($component); + my $config = $self->config->{$suffix} || {}; + + my $instance; + + eval { $instance = $component->new( $context, $config ); }; + + if ( my $error = $@ ) { + + chomp $error; + + Catalyst::Exception->throw( + message => qq/Couldn't instantiate component "$component", "$error"/ + ); + } + + return $instance; + }; + + eval { + Module::Pluggable::Fast->import( + name => '_components', + search => [ + "$self\::Controller", "$self\::C", + "$self\::Model", "$self\::M", + "$self\::View", "$self\::V" + ], + callback => $callback + ); + }; if ( my $error = $@ ) { + chomp $error; - die qq/Couldn't load components "$error"/; + + Catalyst::Exception->throw( + message => qq/Couldn't load components "$error"/ + ); } - $self->components( {} ); - my @comps; - for my $comp ( $self->_components($self) ) { - $self->components->{ ref $comp } = $comp; - push @comps, $comp; + for my $component ( $self->_components($self) ) { + $self->components->{ ref $component || $component } = $component; } - - my $t = Text::ASCIITable->new( { hide_HeadRow => 1, hide_HeadLine => 1 } ); - $t->setCols('Class'); - $t->setColWidth( 'Class', 75, 1 ); - $t->addRow($_) for keys %{ $self->components }; - $self->log->debug( 'Loaded components', $t->draw ) - if ( @{ $t->{tbl_rows} } && $self->debug ); - - $self->setup_actions( [ $self, @comps ] ); } =item $c->state @@ -691,8 +785,8 @@ Returns a hashref containing all your data. sub stash { my $self = shift; - if ( $_[0] ) { - my $stash = $_[1] ? {@_} : $_[0]; + if (@_) { + my $stash = @_ > 1 ? {@_} : $_[0]; while ( my ( $key, $val ) = each %$stash ) { $self->{stash}->{$key} = $val; }