Make headers and cookies non-writable after finalize-headers
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 15b3ea3..55aa48a 100644 (file)
@@ -16,6 +16,8 @@ use Path::Class;
 use Time::HiRes qw/gettimeofday tv_interval/;
 use URI;
 use Scalar::Util qw/weaken/;
+use Hash::Util qw/lock_hash/;
+use HTTP::Headers::ReadOnly;
 use attributes;
 
 __PACKAGE__->mk_accessors(
@@ -54,7 +56,7 @@ __PACKAGE__->engine_class('Catalyst::Engine::CGI');
 __PACKAGE__->request_class('Catalyst::Request');
 __PACKAGE__->response_class('Catalyst::Response');
 
-our $VERSION = '5.5';
+our $VERSION = '5.54';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -667,7 +669,7 @@ EOF
 =item $c->uri_for( $path, [ @args ] )
 
 Merges path with C<$c-E<gt>request-E<gt>base> for absolute uri's and
-with C<$c-E<gt>request-E<gt>match> for relative uri's, then returns a
+with C<$c-E<gt>namespace> for relative uri's, then returns a
 normalized L<URI> object. If any args are passed, they are added at the
 end of the path.
 
@@ -679,18 +681,18 @@ sub uri_for {
     my $basepath = $base->path;
     $basepath =~ s/\/$//;
     $basepath .= '/';
-    my $match = $c->request->match;
+    my $namespace = $c->namespace;
 
-    # massage match, empty if absolute path
-    $match =~ s/^\///;
-    $match .= '/' if $match;
+    # massage namespace, empty if absolute path
+    $namespace =~ s/^\///;
+    $namespace .= '/' if $namespace;
     $path ||= '';
-    $match = '' if $path =~ /^\//;
+    $namespace = '' if $path =~ /^\//;
     $path =~ s/^\///;
 
     # join args with '/', or a blank string
     my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
-    return URI->new_abs( URI->new_abs( "$path$args", "$basepath$match" ),
+    return URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ),
         $base )->canonical;
 }
 
@@ -1023,7 +1025,11 @@ Finalizes cookies.
 
 =cut
 
-sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
+sub finalize_cookies {
+       my $c = shift;
+       $c->engine->finalize_cookies( $c, @_ );
+       lock_hash( %$_ ) for $c->res->cookies, values %{ $c->res->cookies };
+}
 
 =item $c->finalize_error
 
@@ -1066,6 +1072,8 @@ sub finalize_headers {
 
     $c->engine->finalize_headers( $c, @_ );
 
+       bless $c->response->headers, "HTTP::Headers::ReadOnly";
+
     # Done
     $c->response->{_finalized_headers} = 1;
 }
@@ -1496,8 +1504,7 @@ qq/Couldn't instantiate component "$component", "new() didn't return a object"/
         return $instance;
     };
 
-    eval {
-        Module::Pluggable::Fast->import(
+    eval "package $class;\n" . q!Module::Pluggable::Fast->import(
             name   => '_catalyst_components',
             search => [
                 "$class\::Controller", "$class\::C",
@@ -1506,7 +1513,7 @@ qq/Couldn't instantiate component "$component", "new() didn't return a object"/
             ],
             callback => $callback
         );
-    };
+    !;
 
     if ( my $error = $@ ) {
 
@@ -1774,6 +1781,9 @@ Writes $data to the output stream. When using this method directly, you
 will need to manually set the C<Content-Length> header to the length of
 your output data, if known.
 
+Also note that any headers created after the write can  no longer be added, and
+this includes cookies.
+
 =cut
 
 sub write {