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;
-
-require Module::Pluggable::Fast;
-
-# For pretty dumps
-$Data::Dumper::Terse = 1;
+use Catalyst::Utils;
__PACKAGE__->mk_classdata('components');
-__PACKAGE__->mk_accessors(qw/counter request response state/);
+__PACKAGE__->mk_accessors(qw/counter depth request response state/);
*comp = \&component;
*req = \&request;
our $COUNT = 1;
our $START = time;
our $RECURSION = 1000;
+our $DETACH = "catalyst_detach\n";
=head1 NAME
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, ...)
$action = "-> $action" if $callsub =~ /forward$/;
}
+ $c->{depth}++;
eval {
if ( $c->debug )
{
- my ( $elapsed, @state ) = $c->benchmark( $code, $class, $c, @{ $c->req->args } );
+ 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 } ) ? 1 : 0 ) }
+ 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;
}
$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;
my ( $title, $error, $infos );
if ( $c->debug ) {
- $error = join '<br/>', @{ $c->error };
+
+ # For pretty dumps
+ local $Data::Dumper::Terse = 1;
+ $error = join '',
+ map { '<code class="error">' . encode_entities($_) . '</code>' }
+ @{ $c->error };
$error ||= 'No output';
$title = $name = "$name on Catalyst $Catalyst::VERSION";
my $req = encode_entities Dumper $c->req;
margin: 4px;
-moz-border-radius: 10px;
}
+ code.error {
+ display: block;
+ margin: 1em 0;
+ overflow: auto;
+ white-space: pre;
+ }
</style>
</head>
<body>
$t->setColWidth( 'Time', 9, 1 );
for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) }
- $class->log->info( "Request took $elapsed" . "s ($av/s)",
- $t->draw );
+ $class->log->info(
+ "Request took ${elapsed}s ($av/s)\n" . $t->draw );
}
else { $status = &$handler }
my $c = bless {
counter => {},
+ depth => 0,
request => Catalyst::Request->new(
{
arguments => [],
{
body => '',
cookies => {},
- headers => HTTP::Headers->new,
+ headers => HTTP::Headers->new( 'Content-Length' => 0 ),
status => 200
}
),
$c->prepare_path;
$c->prepare_action;
- my $method = $c->req->method || '';
- my $path = $c->req->path || '';
- 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 $address/)
if $c->debug;
my $value = defined($param) ? $param : '';
$t->addRow( $key, $value );
}
- $c->log->debug( 'Parameters are', $t->draw );
+ $c->log->debug( "Parameters are:\n" . $t->draw );
}
return $c;
my $res = $c->res;
-=item $class->setup
-
-Setup.
-
- MyApp->setup;
-
-=cut
-
-sub setup {
- my $self = shift;
- $self->setup_components;
- if ( $self->debug ) {
- my $name = $self->config->{name} || 'Application';
- $self->log->info("$name powered by Catalyst $Catalyst::VERSION");
- }
-}
-
-=item $class->setup_components
-
-Setup components.
-
-=cut
-
-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'
- ];
-
- if ( my $error = $@ ) {
- chomp $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($_) for sort keys %{ $self->components };
- $self->log->debug( 'Loaded components', $t->draw )
- if ( @{ $t->{tbl_rows} } && $self->debug );
-
- $self->setup_actions( [ $self, @comps ] );
-}
-
=item $c->state
Contains the return value of the last executed action.