Merge remote branch 'origin/no_state_in_engine'
Tomas Doran [Sun, 22 Jan 2012 09:50:54 +0000 (09:50 +0000)]
* origin/no_state_in_engine: (22 commits)
  silent warnings from Data::Dumper about dummy CODE refs
  silence warning from Engine::HTTP when $ENV{HARNESS_ACTIVE} is true
  Spelling skips
  Work when not aggregated
  Pod spelling
  Fix removed methods that plugins are likely to be hooking
  Fix docs in Response, fix Pod tests
  Sort out the Request docs
  Start re-arranging and fixing docs. remove docs for deprecated stuff
  Stop the request needing the context, just pass in the logger instead
  The response no longer needs the context
  Move write and finalize_headers into response object
  Put prepare_connection back as Engine::PSGI uses it
  Do moar, moving headers and cookies. This breaks engine::psgi, fix later..
  Move prepare_connection, and it's lies documentation. Bet this breaks mad engines (stomp?)
  Move prepare_parametrs to be the builder.
  Move preparing the body into the request, almost works.
  Move actual reading into request
  Move read_chunk to the request
  Similarly, we don't need finalize_read
  ...

1  2 
lib/Catalyst.pm
lib/Catalyst/Engine.pm

diff --combined lib/Catalyst.pm
@@@ -46,8 -46,24 +46,24 @@@ has state => (is => 'rw', default => 0)
  has stats => (is => 'rw');
  has action => (is => 'rw');
  has counter => (is => 'rw', default => sub { {} });
- has request => (is => 'rw', default => sub { $_[0]->request_class->new({}) }, required => 1, lazy => 1);
- has response => (is => 'rw', default => sub { $_[0]->response_class->new({}) }, required => 1, lazy => 1);
+ has request => (
+     is => 'rw',
+     default => sub {
+         my $self = shift;
+         my %p = ( _log => $self->log );
+         $p{_uploadtmp} = $self->_uploadtmp if $self->_has_uploadtmp;
+         $self->request_class->new(\%p);
+     },
+     lazy => 1,
+ );
+ has response => (
+     is => 'rw',
+     default => sub {
+         my $self = shift;
+         $self->response_class->new({ _log => $self->log });
+     },
+     lazy => 1,
+ );
  has namespace => (is => 'rw');
  
  sub depth { scalar @{ shift->stack || [] }; }
@@@ -84,7 -100,7 +100,7 @@@ __PACKAGE__->stats_class('Catalyst::Sta
  
  # Remember to update this in Catalyst::Runtime as well!
  
 -our $VERSION = '5.90006';
 +our $VERSION = '5.90007';
  
  sub import {
      my ( $class, @arguments ) = @_;
@@@ -356,12 -372,8 +372,12 @@@ When called with no arguments it escape
  
  sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
  
 +=head2 $c->visit( $action [, \@arguments ] )
 +
  =head2 $c->visit( $action [, \@captures, \@arguments ] )
  
 +=head2 $c->visit( $class, $method, [, \@arguments ] )
 +
  =head2 $c->visit( $class, $method, [, \@captures, \@arguments ] )
  
  Almost the same as L<< forward|/"$c->forward( $action [, \@arguments ] )" >>,
@@@ -390,12 -402,8 +406,12 @@@ transfer control to another action as i
  
  sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) }
  
 +=head2 $c->go( $action [, \@arguments ] )
 +
  =head2 $c->go( $action [, \@captures, \@arguments ] )
  
 +=head2 $c->go( $class, $method, [, \@arguments ] )
 +
  =head2 $c->go( $class, $method, [, \@captures, \@arguments ] )
  
  The relationship between C<go> and
@@@ -1991,6 -1999,11 +2007,11 @@@ etc.)
  
  =cut
  
+ has _uploadtmp => (
+     is => 'ro',
+     predicate => '_has_uploadtmp',
+ );
  sub prepare {
      my ( $class, @arguments ) = @_;
  
      # into the application.
      $class->context_class( ref $class || $class ) unless $class->context_class;
  
-     my $c = $class->context_class->new({});
-     # For on-demand data
-     $c->request->_context($c);
-     $c->response->_context($c);
+     my $uploadtmp = $class->config->{uploadtmp};
+     my $c = $class->context_class->new({ $uploadtmp ? (_uploadtmp => $uploadtmp) : ()});
  
      #surely this is not the most efficient way to do things...
      $c->stats($class->stats_class->new)->enable($c->use_stats);
              $c->prepare_request(@arguments);
              $c->prepare_connection;
              $c->prepare_query_parameters;
-             $c->prepare_headers;
-             $c->prepare_cookies;
+             $c->prepare_headers; # Just hooks, no longer needed - they just
+             $c->prepare_cookies; # cause the lazy attribute on req to build
              $c->prepare_path;
  
              # Prepare the body for reading, either by prepare_body
                  $c->prepare_body;
              }
          }
 +        $c->prepare_action;
      }
      # VERY ugly and probably shouldn't rely on ->finalize actually working
      catch {
          $c->response->status(400);
          $c->response->content_type('text/plain');
          $c->response->body('Bad Request');
 +        # Note we call finalize and then die here, which escapes
 +        # finalize being called in the enclosing block..
 +        # It in fact couldn't be called, as we don't return $c..
 +        # This is a mess - but I'm unsure you can fix this without
 +        # breaking compat for people doing crazy things (we should set
 +        # the 400 and just return the ctx here IMO, letting finalize get called
 +        # above...
          $c->finalize;
          die $_;
      };
  
 -    my $method  = $c->req->method  || '';
 -    my $path    = $c->req->path;
 -    $path       = '/' unless length $path;
 -    my $address = $c->req->address || '';
 -
      $c->log_request;
  
 -    $c->prepare_action;
 -
      return $c;
  }
  
@@@ -2114,24 -2123,28 +2132,28 @@@ Prepares connection
  
  sub prepare_connection {
      my $c = shift;
-     $c->engine->prepare_connection( $c, @_ );
+     # XXX - This is called on the engine (not the request) to maintain
+     #       Engine::PSGI back compat.
+     $c->engine->prepare_connection($c);
  }
  
  =head2 $c->prepare_cookies
  
- Prepares cookies.
+ Prepares cookies by ensuring that the attribute on the request
+ object has been built.
  
  =cut
  
- sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
+ sub prepare_cookies { my $c = shift; $c->request->cookies }
  
  =head2 $c->prepare_headers
  
- Prepares headers.
+ Prepares request headers by ensuring that the attribute on the request
+ object has been built.
  
  =cut
  
- sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
+ sub prepare_headers { my $c = shift; $c->request->headers }
  
  =head2 $c->prepare_parameters
  
@@@ -2414,7 -2427,7 +2436,7 @@@ $c->request.  You must handle all body 
  
  =cut
  
- sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
+ sub read { my $c = shift; return $c->request->read( @_ ) }
  
  =head2 $c->run
  
@@@ -2752,16 -2765,7 +2774,16 @@@ sub apply_default_middlewares 
  
      # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME
      # http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html
 -    $psgi_app = Plack::Middleware::LighttpdScriptNameFix->wrap($psgi_app);
 +    $psgi_app = Plack::Middleware::Conditional->wrap(
 +        $psgi_app,
 +        builder   => sub { Plack::Middleware::LighttpdScriptNameFix->wrap($_[0]) },
 +        condition => sub {
 +            my ($env) = @_;
 +            return unless $env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!lighttpd[-/]1\.(\d+\.\d+)!;
 +            return unless $1 < 4.23;
 +            1;
 +        },
 +    );
  
      # we're applying this unconditionally as the middleware itself already makes
      # sure it doesn't fuck things up if it's not running under one of the right
@@@ -2992,10 -2996,10 +3014,10 @@@ your output data, if known
  sub write {
      my $c = shift;
  
-     # Finalize headers if someone manually writes output
+     # Finalize headers if someone manually writes output (for compat)
      $c->finalize_headers;
  
-     return $c->engine->write( $c, @_ );
+     return $c->response->write( @_ );
  }
  
  =head2 version
@@@ -3243,6 -3247,8 +3265,6 @@@ Wiki
  
  =head2 L<Catalyst::Test> - The test suite.
  
 -=begin stopwords
 -
  =head1 PROJECT FOUNDER
  
  sri: Sebastian Riedel <sri@cpan.org>
@@@ -3385,6 -3391,8 +3407,6 @@@ rainboxx: Matthias Dietrich, C<perl@rai
  
  dd070: Dhaval Dhanani <dhaval070@gmail.com>
  
 -=end stopwords
 -
  =head1 COPYRIGHT
  
  Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS.
diff --combined lib/Catalyst/Engine.pm
@@@ -10,7 -10,6 +10,6 @@@ use HTML::Entities
  use HTTP::Body;
  use HTTP::Headers;
  use URI::QueryParam;
- use Moose::Util::TypeConstraints;
  use Plack::Loader;
  use Catalyst::EngineLoader;
  use Encode ();
@@@ -18,8 -17,11 +17,11 @@@ use utf8
  
  use namespace::clean -except => 'meta';
  
- has env => (is => 'ro', writer => '_set_env', clearer => '_clear_env');
+ # Amount of data to read from input on each pass
+ our $CHUNKSIZE = 64 * 1024;
  
+ # XXX - this is only here for compat, do not use!
+ has env => ( is => 'rw', writer => '_set_env' );
  my $WARN_ABOUT_ENV = 0;
  around env => sub {
    my ($orig, $self, @args) = @_;
    return $self->$orig;
  };
  
- # input position and length
- has read_length => (is => 'rw');
- has read_position => (is => 'rw');
- has _prepared_write => (is => 'rw');
- has _response_cb => (
-     is      => 'ro',
-     isa     => 'CodeRef',
-     writer  => '_set_response_cb',
-     clearer => '_clear_response_cb',
-     predicate => '_has_response_cb',
- );
- subtype 'Catalyst::Engine::Types::Writer',
-     as duck_type([qw(write close)]);
- has _writer => (
-     is      => 'ro',
-     isa     => 'Catalyst::Engine::Types::Writer',
-     writer  => '_set_writer',
-     clearer => '_clear_writer',
- );
- # Amount of data to read from input on each pass
- our $CHUNKSIZE = 64 * 1024;
+ # XXX - Only here for Engine::PSGI compat
+ sub prepare_connection {
+     my ($self, $ctx) = @_;
+     $ctx->request->prepare_connection;
+ }
  
  =head1 NAME
  
@@@ -94,9 -75,9 +75,9 @@@ sub finalize_body 
          $self->write( $c, $body );
      }
  
-     $self->_writer->close;
-     $self->_clear_writer;
-     $self->_clear_env;
+     my $res = $c->response;
+     $res->_writer->close;
+     $res->_clear_writer;
  
      return;
  }
@@@ -344,37 -325,17 +325,17 @@@ sub finalize_error 
  
  =head2 $self->finalize_headers($c)
  
- Abstract method, allows engines to write headers to response
+ Allows engines to write headers to response
  
  =cut
  
  sub finalize_headers {
      my ($self, $ctx) = @_;
  
-     # This is a less-than-pretty hack to avoid breaking the old
-     # Catalyst::Engine::PSGI. 5.9 Catalyst::Engine sets a response_cb and
-     # expects us to pass headers to it here, whereas Catalyst::Enngine::PSGI
-     # just pulls the headers out of $ctx->response in its run method and never
-     # sets response_cb. So take the lack of a response_cb as a sign that we
-     # don't need to set the headers.
-     return unless $self->_has_response_cb;
-     my @headers;
-     $ctx->response->headers->scan(sub { push @headers, @_ });
-     $self->_set_writer($self->_response_cb->([ $ctx->response->status, \@headers ]));
-     $self->_clear_response_cb;
+     $ctx->response->finalize_headers;
      return;
  }
  
- =head2 $self->finalize_read($c)
- =cut
- sub finalize_read { }
  =head2 $self->finalize_uploads($c)
  
  Clean up after uploads, deleting temp files.
@@@ -404,34 -365,7 +365,7 @@@ sets up the L<Catalyst::Request> objec
  sub prepare_body {
      my ( $self, $c ) = @_;
  
-     my $appclass = ref($c) || $c;
-     if ( my $length = $self->read_length ) {
-         my $request = $c->request;
-         unless ( $request->_body ) {
-             my $type = $request->header('Content-Type');
-             $request->_body(HTTP::Body->new( $type, $length ));
-             $request->_body->cleanup(1); # Make extra sure!
-             $request->_body->tmpdir( $appclass->config->{uploadtmp} )
-               if exists $appclass->config->{uploadtmp};
-         }
-         # Check for definedness as you could read '0'
-         while ( defined ( my $buffer = $self->read($c) ) ) {
-             $c->prepare_body_chunk($buffer);
-         }
-         # paranoia against wrong Content-Length header
-         my $remaining = $length - $self->read_position;
-         if ( $remaining > 0 ) {
-             $self->finalize_read($c);
-             Catalyst::Exception->throw(
-                 "Wrong Content-Length value: $length" );
-         }
-     }
-     else {
-         # Defined but will cause all body code to be skipped
-         $c->request->_body(0);
-     }
+     $c->request->prepare_body;
  }
  
  =head2 $self->prepare_body_chunk($c)
@@@ -440,10 -374,11 +374,11 @@@ Add a chunk to the request body
  
  =cut
  
+ # XXX - Can this be deleted?
  sub prepare_body_chunk {
      my ( $self, $c, $chunk ) = @_;
  
-     $c->request->_body->add($chunk);
+     $c->request->prepare_body_chunk($chunk);
  }
  
  =head2 $self->prepare_body_parameters($c)
@@@ -455,64 -390,7 +390,7 @@@ Sets up parameters from body
  sub prepare_body_parameters {
      my ( $self, $c ) = @_;
  
-     return unless $c->request->_body;
-     $c->request->body_parameters( $c->request->_body->param );
- }
- =head2 $self->prepare_connection($c)
- Abstract method implemented in engines.
- =cut
- sub prepare_connection {
-     my ($self, $ctx) = @_;
-     my $env = $self->env;
-     my $request = $ctx->request;
-     $request->address( $env->{REMOTE_ADDR} );
-     $request->hostname( $env->{REMOTE_HOST} )
-         if exists $env->{REMOTE_HOST};
-     $request->protocol( $env->{SERVER_PROTOCOL} );
-     $request->remote_user( $env->{REMOTE_USER} );
-     $request->method( $env->{REQUEST_METHOD} );
-     $request->secure( $env->{'psgi.url_scheme'} eq 'https' ? 1 : 0 );
-     return;
- }
- =head2 $self->prepare_cookies($c)
- Parse cookies from header. Sets a L<CGI::Simple::Cookie> object.
- =cut
- sub prepare_cookies {
-     my ( $self, $c ) = @_;
-     if ( my $header = $c->request->header('Cookie') ) {
-         $c->req->cookies( { CGI::Simple::Cookie->parse($header) } );
-     }
- }
- =head2 $self->prepare_headers($c)
- =cut
- sub prepare_headers {
-     my ($self, $ctx) = @_;
-     my $env = $self->env;
-     my $headers = $ctx->request->headers;
-     for my $header (keys %{ $env }) {
-         next unless $header =~ /^(HTTP|CONTENT|COOKIE)/i;
-         (my $field = $header) =~ s/^HTTPS?_//;
-         $field =~ tr/_/-/;
-         $headers->header($field => $env->{$header});
-     }
+     $c->request->prepare_body_parameters;
  }
  
  =head2 $self->prepare_parameters($c)
@@@ -524,25 -402,7 +402,7 @@@ sets up parameters from query and post 
  sub prepare_parameters {
      my ( $self, $c ) = @_;
  
-     my $request = $c->request;
-     my $parameters = $request->parameters;
-     my $body_parameters = $request->body_parameters;
-     my $query_parameters = $request->query_parameters;
-     # We copy, no references
-     foreach my $name (keys %$query_parameters) {
-         my $param = $query_parameters->{$name};
-         $parameters->{$name} = ref $param eq 'ARRAY' ? [ @$param ] : $param;
-     }
-     # Merge query and body parameters
-     foreach my $name (keys %$body_parameters) {
-         my $param = $body_parameters->{$name};
-         my @values = ref $param eq 'ARRAY' ? @$param : ($param);
-         if ( my $existing = $parameters->{$name} ) {
-           unshift(@values, (ref $existing eq 'ARRAY' ? @$existing : $existing));
-         }
-         $parameters->{$name} = @values > 1 ? \@values : $values[0];
-     }
+     $c->request->parameters;
  }
  
  =head2 $self->prepare_path($c)
@@@ -554,7 -414,7 +414,7 @@@ abstract method, implemented by engines
  sub prepare_path {
      my ($self, $ctx) = @_;
  
-     my $env = $self->env;
+     my $env = $ctx->request->env;
  
      my $scheme    = $ctx->request->secure ? 'https' : 'http';
      my $host      = $env->{HTTP_HOST} || $env->{SERVER_NAME};
@@@ -618,8 -478,9 +478,9 @@@ process the query string and extract qu
  sub prepare_query_parameters {
      my ($self, $c) = @_;
  
-     my $query_string = exists $self->env->{QUERY_STRING}
-         ? $self->env->{QUERY_STRING}
+     my $env = $c->request->env;
+     my $query_string = exists $env->{QUERY_STRING}
+         ? $env->{QUERY_STRING}
          : '';
  
      # Check for keywords (no = signs)
              $query{$param} = $value;
          }
      }
      $c->request->query_parameters( \%query );
  }
  
  =head2 $self->prepare_read($c)
  
- prepare to read from the engine.
+ Prepare to read by initializing the Content-Length from headers.
  
  =cut
  
  sub prepare_read {
      my ( $self, $c ) = @_;
  
-     # Initialize the read position
-     $self->read_position(0);
      # Initialize the amount of data we think we need to read
-     $self->read_length( $c->request->header('Content-Length') || 0 );
+     $c->request->_read_length;
  }
  
  =head2 $self->prepare_request(@arguments)
  
 -Populate the context object from the request object.
 +Sets up the PSGI environment in the Engine.
  
  =cut
  
  sub prepare_request {
      my ($self, $ctx, %args) = @_;
-     $self->_set_env($args{env});
+     $ctx->request->_set_env($args{env});
+     $self->_set_env($args{env}); # Nasty back compat!
+     $ctx->response->_set_response_cb($args{response_cb});
  }
  
  =head2 $self->prepare_uploads($c)
@@@ -733,13 -592,17 +592,17 @@@ sub prepare_uploads 
      }
  }
  
- =head2 $self->prepare_write($c)
+ =head2 $self->write($c, $buffer)
  
- Abstract method. Implemented by the engines.
+ Writes the buffer to the client.
  
  =cut
  
- sub prepare_write { }
+ sub write {
+     my ( $self, $c, $buffer ) = @_;
+     $c->response->write($buffer);
+ }
  
  =head2 $self->read($c, [$maxlength])
  
@@@ -752,33 -615,10 +615,10 @@@ Maintains the read_length and read_posi
  sub read {
      my ( $self, $c, $maxlength ) = @_;
  
-     my $remaining = $self->read_length - $self->read_position;
-     $maxlength ||= $CHUNKSIZE;
-     # Are we done reading?
-     if ( $remaining <= 0 ) {
-         $self->finalize_read($c);
-         return;
-     }
-     my $readlen = ( $remaining > $maxlength ) ? $maxlength : $remaining;
-     my $rc = $self->read_chunk( $c, my $buffer, $readlen );
-     if ( defined $rc ) {
-         if (0 == $rc) { # Nothing more to read even though Content-Length
-                         # said there should be.
-             $self->finalize_read;
-             return;
-         }
-         $self->read_position( $self->read_position + $rc );
-         return $buffer;
-     }
-     else {
-         Catalyst::Exception->throw(
-             message => "Unknown error reading input: $!" );
-     }
+     $c->request->read($maxlength);
  }
  
- =head2 $self->read_chunk($c, $buffer, $length)
+ =head2 $self->read_chunk($c, \$buffer, $length)
  
  Each engine implements read_chunk as its preferred way of reading a chunk
  of data. Returns the number of bytes read. A return of 0 indicates that
@@@ -788,18 -628,9 +628,9 @@@ there is no more data to be read
  
  sub read_chunk {
      my ($self, $ctx) = (shift, shift);
-     return $self->env->{'psgi.input'}->read(@_);
+     return $ctx->request->read_chunk(@_);
  }
  
- =head2 $self->read_length
- The length of input data to be read.  This is obtained from the Content-Length
- header.
- =head2 $self->read_position
- The amount of input data that has already been read.
  =head2 $self->run($app, $server)
  
  Start the engine. Builds a PSGI application and calls the
@@@ -839,7 -670,8 +670,7 @@@ sub run 
  
  =head2 build_psgi_app ($app, @args)
  
 -Builds and returns a PSGI application closure, wrapping it in the reverse proxy
 -middleware if the using_frontend_proxy config setting is set.
 +Builds and returns a PSGI application closure. (Raw, not wrapped in middleware)
  
  =cut
  
@@@ -851,34 -683,11 +682,11 @@@ sub build_psgi_app 
  
          return sub {
              my ($respond) = @_;
-             $self->_set_response_cb($respond);
-             $app->handle_request(env => $env);
+             $app->handle_request(env => $env, response_cb => $respond);
          };
      };
  }
  
- =head2 $self->write($c, $buffer)
- Writes the buffer to the client.
- =cut
- sub write {
-     my ( $self, $c, $buffer ) = @_;
-     unless ( $self->_prepared_write ) {
-         $self->prepare_write($c);
-         $self->_prepared_write(1);
-     }
-     $buffer = q[] unless defined $buffer;
-     my $len = length($buffer);
-     $self->_writer->write($buffer);
-     return $len;
- }
  =head2 $self->unescape_uri($uri)
  
  Unescapes a given URI using the most efficient method available.  Engines such