From: Tomas Doran Date: Sun, 22 Jan 2012 09:50:54 +0000 (+0000) Subject: Merge remote branch 'origin/no_state_in_engine' X-Git-Tag: 5.90008~16 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=d42e2887f057321d1e65aaffb7ee822d9c1525f3;hp=-c Merge remote branch 'origin/no_state_in_engine' * 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 ... --- d42e2887f057321d1e65aaffb7ee822d9c1525f3 diff --combined lib/Catalyst.pm index 45cb6ff,d458962..ebfcf8c --- a/lib/Catalyst.pm +++ b/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 and @@@ -1991,6 -1999,11 +2007,11 @@@ etc.) =cut + has _uploadtmp => ( + is => 'ro', + predicate => '_has_uploadtmp', + ); + sub prepare { my ( $class, @arguments ) = @_; @@@ -1999,11 -2012,8 +2020,8 @@@ # 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); @@@ -2020,8 -2030,8 +2038,8 @@@ $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 @@@ -2033,7 -2043,6 +2051,7 @@@ $c->prepare_body; } } + $c->prepare_action; } # VERY ugly and probably shouldn't rely on ->finalize actually working catch { @@@ -2041,19 -2050,19 +2059,19 @@@ $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 - The test suite. -=begin stopwords - =head1 PROJECT FOUNDER sri: Sebastian Riedel @@@ -3385,6 -3391,8 +3407,6 @@@ rainboxx: Matthias Dietrich, C -=end stopwords - =head1 COPYRIGHT Copyright (c) 2005, the above named PROJECT FOUNDER and CONTRIBUTORS. diff --combined lib/Catalyst/Engine.pm index 8f88cef,fac70cd..48126e7 --- a/lib/Catalyst/Engine.pm +++ b/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) = @_; @@@ -31,32 -33,11 +33,11 @@@ 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 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 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) @@@ -656,35 -517,33 +517,33 @@@ $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