X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst%2FEngine.pm;h=cbc5896993ba66a413b1b9f282eac72ede2957c2;hp=9b7d58ab130aa184f76a8c3a3f96de29837fa909;hb=8fbcd90cdb30aed53d22d1cdbad95880f1c11693;hpb=5fbed090416a5800ae1b0670b9b5d480b7ca9df8 diff --git a/lib/Catalyst/Engine.pm b/lib/Catalyst/Engine.pm index 9b7d58a..cbc5896 100644 --- a/lib/Catalyst/Engine.pm +++ b/lib/Catalyst/Engine.pm @@ -2,6 +2,7 @@ package Catalyst::Engine; use strict; use base qw/Class::Data::Inheritable Class::Accessor::Fast/; +use attributes (); use UNIVERSAL::require; use CGI::Cookie; use Data::Dumper; @@ -9,13 +10,13 @@ use HTML::Entities; use HTTP::Headers; use Time::HiRes qw/gettimeofday tv_interval/; use Text::ASCIITable; -use Text::ASCIITable::Wrap 'wrap'; use Catalyst::Request; use Catalyst::Request::Upload; use Catalyst::Response; require Module::Pluggable::Fast; +# For pretty dumps $Data::Dumper::Terse = 1; __PACKAGE__->mk_classdata('components'); @@ -25,6 +26,10 @@ __PACKAGE__->mk_accessors(qw/request response state/); *req = \&request; *res = \&response; +# For backwards compatibility +*finalize_output = \&finalize_body; + +# For statistics our $COUNT = 1; our $START = time; @@ -76,14 +81,17 @@ Regex search for a component. sub component { my ( $c, $name ) = @_; + if ( my $component = $c->components->{$name} ) { return $component; } + else { for my $component ( keys %{ $c->components } ) { return $c->components->{$component} if $component =~ /$name/i; } } + } =item $c->error @@ -118,9 +126,10 @@ 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]; + eval { if ( $c->debug ) { @@ -134,6 +143,7 @@ sub execute { } else { $c->state( &$code( $class, $c, @{ $c->req->args } ) ) } }; + if ( my $error = $@ ) { unless ( ref $error ) { @@ -169,20 +179,31 @@ sub finalize { $c->finalize_error; } - if ( !$c->response->output && $c->response->status !~ /^(1|3)\d\d$/ ) { + if ( !$c->response->body_length && $c->response->status !~ /^(1|3)\d\d$/ ) { $c->finalize_error; } - if ( $c->response->output && !$c->response->content_length ) { - use bytes; # play safe with a utf8 aware perl - $c->response->content_length( length $c->response->output ); + if ( $c->response->body_length && !$c->response->content_length ) { + $c->response->content_length( $c->response->body_length ); } my $status = $c->finalize_headers; - $c->finalize_output; + $c->finalize_body; return $status; } +=item $c->finalize_output + +alias to finalize_body + +=item $c->finalize_body + +Finalize body. + +=cut + +sub finalize_body { } + =item $c->finalize_cookies Finalize cookies. @@ -253,7 +274,7 @@ sub finalize_error { $name = ''; } - $c->res->output( <<"" ); + $c->res->body( <<"" ); $title @@ -317,15 +338,7 @@ Finalize headers. sub finalize_headers { } -=item $c->finalize_output - -Finalize output. - -=cut - -sub finalize_output { } - -=item $c->handler( $class, $r ) +=item $c->handler( $class, $engine ) Handles the request. @@ -338,39 +351,42 @@ sub handler { my $status = -1; eval { my @stats = (); + my $handler = sub { my $c = $class->prepare($engine); $c->{stats} = \@stats; $c->dispatch; return $c->finalize; }; + if ( $class->debug ) { 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 ); $t->setColWidth( 'Time', 9, 1 ); - for my $stat (@stats) { - $t->addRow( wrap( $stat->[0], 64 ), wrap( $stat->[1], 9 ) ); - } + for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) } $class->log->info( "Request took $elapsed" . "s ($av/s)", $t->draw ); } else { $status = &$handler } + }; + if ( my $error = $@ ) { chomp $error; $class->log->error(qq/Caught exception in engine "$error"/); } + $COUNT++; return $status; } -=item $c->prepare($r) +=item $c->prepare($engine) Turns the engine-specific request( Apache, CGI ... ) into a Catalyst context . @@ -378,11 +394,13 @@ into a Catalyst context . =cut sub prepare { - my ( $class, $r ) = @_; + my ( $class, $engine ) = @_; + my $c = bless { request => Catalyst::Request->new( { arguments => [], + body => undef, cookies => {}, headers => HTTP::Headers->new, parameters => {}, @@ -391,11 +409,17 @@ sub prepare { } ), response => Catalyst::Response->new( - { cookies => {}, headers => HTTP::Headers->new, status => 200 } + { + body => undef, + cookies => {}, + headers => HTTP::Headers->new, + status => 200 + } ), stash => {}, state => 0 }, $class; + if ( $c->debug ) { my $secs = time - $START || 1; my $av = sprintf '%.3f', $COUNT / $secs; @@ -404,32 +428,53 @@ sub prepare { $c->log->debug('**********************************'); $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION ); } - $c->prepare_request($r); - $c->prepare_path; + + $c->prepare_request($engine); + $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 || ''; + $c->log->debug(qq/"$method" request for "$path" from $hostname($address)/) if $c->debug; - $c->prepare_action; - $c->prepare_parameters; + + if ( $c->request->method eq 'POST' and $c->request->content_length ) { + + if ( $c->req->content_type eq 'application/x-www-form-urlencoded' ) { + $c->prepare_parameters; + } + elsif ( $c->req->content_type eq 'multipart/form-data' ) { + $c->prepare_parameters; + $c->prepare_uploads; + } + else { + $c->prepare_body; + } + } + + if ( $c->request->method eq 'GET' ) { + $c->prepare_parameters; + } if ( $c->debug && keys %{ $c->req->params } ) { my $t = Text::ASCIITable->new; $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} || ''; - $t->addRow( wrap( $key, 37 ), wrap( $value, 36 ) ); + 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 ); } - $c->prepare_uploads; + return $c; } @@ -444,6 +489,7 @@ sub prepare_action { my $path = $c->req->path; my @path = split /\//, $c->req->path; $c->req->args( \my @args ); + while (@path) { $path = join '/', @path; if ( my $result = ${ $c->get_action($path) }[0] ) { @@ -461,23 +507,35 @@ sub prepare_action { $c->req->action($match); $c->req->snippets( \@snippets ); } + else { $c->req->action($path); $c->log->debug(qq/Requested action is "$path"/) if $c->debug; } + $c->req->match($path); last; } unshift @args, pop @path; } + unless ( $c->req->action ) { $c->req->action('default'); $c->req->match(''); } + $c->log->debug( 'Arguments are "' . join( '/', @args ) . '"' ) if ( $c->debug && @args ); } +=item $c->prepare_body + +Prepare message body. + +=cut + +sub prepare_body { } + =item $c->prepare_connection Prepare connection. @@ -604,21 +662,23 @@ sub setup_components { if ( my $error = $@ ) { chomp $error; - $self->log->error( - qq/Couldn't initialize "Module::Pluggable::Fast", "$error"/); + die qq/Couldn't load components "$error"/; } + $self->components( {} ); my @comps; for my $comp ( $self->_components($self) ) { $self->components->{ ref $comp } = $comp; push @comps, $comp; } + my $t = Text::ASCIITable->new( { hide_HeadRow => 1, hide_HeadLine => 1 } ); $t->setCols('Class'); $t->setColWidth( 'Class', 75, 1 ); - $t->addRow( wrap( $_, 75 ) ) for keys %{ $self->components }; + $t->addRow($_) for keys %{ $self->components }; $self->log->debug( 'Loaded components', $t->draw ) if ( @{ $t->{tbl_rows} } && $self->debug ); + $self->setup_actions( [ $self, @comps ] ); }