X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=d6717cb97ee642b0647897008510165b7d4fcc21;hp=6096f4789afe4569ea0438fa84b2363d594736f7;hb=67fd25bc03bc87a19db237a13fcb1cd157252101;hpb=b9815ce64019ef9726acb5cf1646d9bf8af5997f diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 6096f47..d6717cb 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -50,7 +50,7 @@ use Plack::Middleware::RemoveRedundantBody; use Catalyst::Middleware::Stash; use Plack::Util; use Class::Load 'load_class'; -use Encode 2.21 (); +use Encode 2.21 'decode_utf8', 'encode_utf8'; BEGIN { require 5.008003; } @@ -86,8 +86,10 @@ has response => ( lazy => 1, ); sub _build_response_constructor_args { - my $self = shift; - { _log => $self->log }; + return +{ + _log => $_[0]->log, + encoding => $_[0]->encoding, + }; } has namespace => (is => 'rw'); @@ -118,7 +120,7 @@ __PACKAGE__->mk_classdata($_) for qw/components arguments dispatcher engine log dispatcher_class engine_loader context_class request_class response_class stats_class setup_finished _psgi_app loading_psgi_file run_options _psgi_middleware - _data_handlers _encoding _encode_check/; + _data_handlers _encoding _encode_check finalized_default_middleware/; __PACKAGE__->dispatcher_class('Catalyst::Dispatcher'); __PACKAGE__->request_class('Catalyst::Request'); @@ -127,7 +129,8 @@ __PACKAGE__->stats_class('Catalyst::Stats'); __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.90077'; +our $VERSION = '5.90082'; +$VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases sub import { my ( $class, @arguments ) = @_; @@ -495,6 +498,18 @@ Catalyst). # stash is automatically passed to the view for use in a template $c->forward( 'MyApp::View::TT' ); +The stash hash is currently stored in the PSGI C<$env> and is managed by +L. Since it's part of the C<$env> items in +the stash can be accessed in sub applications mounted under your main +L application. For example if you delegate the response of an +action to another L application, that sub application will have +access to all the stash keys of the main one, and if can of course add +more keys of its own. However those new keys will not 'bubble' back up +to the main application. + +For more information the best thing to do is to review the test case: +t/middleware-stash.t in the distribution /t directory. + =cut sub stash { @@ -577,7 +592,7 @@ sub last_error { my ($err, @errs) = @{shift->error}; return $err } =head2 shift_errors shifts the most recently added error off the error stack and returns if. Returns -nothing if there are nomore errors. +nothing if there are no more errors. =cut @@ -1024,17 +1039,47 @@ And later: Your log class should implement the methods described in L. +=head2 has_encoding + +Returned True if there's a valid encoding + +=head2 clear_encoding + +Clears the encoding for the current context + =head2 encoding -Sets or gets the application encoding. +Sets or gets the application encoding. Setting encoding takes either an +Encoding object or a string that we try to resolve via L. + +You would expect to get the encoding object back if you attempt to set it. If +there is a failure you will get undef returned and an error message in the log. =cut +sub has_encoding { shift->encoding ? 1:0 } + +sub clear_encoding { + my $c = shift; + if(blessed $c) { + $c->encoding(undef); + } else { + $c->log->error("You can't clear encoding on the application"); + } +} + sub encoding { my $c = shift; my $encoding; if ( scalar @_ ) { + + # Don't let one change this once we are too far into the response + if(blessed $c && $c->res->finalized_headers) { + Carp::croak("You may not change the encoding once the headers are finalized"); + return; + } + # Let it be set to undef if (my $wanted = shift) { $encoding = Encode::find_encoding($wanted) @@ -1150,6 +1195,17 @@ Catalyst> line. B You B wrap this method with method modifiers or bad things will happen - wrap the C method instead. +B You can create a custom setup stage that will execute when the +application is starting. Use this to customize setup. + + MyApp->setup(-Custom=value); + + sub setup_custom { + my ($class, $value) = @_; + } + +Can be handy if you want to hook into the setup phase. + =cut sub setup { @@ -1346,6 +1402,8 @@ sub setup_finalize { =head2 $c->uri_for( $action, \@captures?, @args?, \%query_values? ) +=head2 $c->uri_for( $action, [@captures, @args], \%query_values? ) + Constructs an absolute L object based on the application root, the provided path, and the additional arguments and query parameters provided. When used as a string, provides a textual URI. If you need more flexibility @@ -1385,6 +1443,10 @@ path, use C<< $c->uri_for_action >> instead. # Path to a static resource $c->uri_for('/static/images/logo.png'); +In general the scheme of the generated URI object will follow the incoming request +however if your targeted action or action chain has the Scheme attribute it will +use that instead. + =cut sub uri_for { @@ -1402,30 +1464,38 @@ sub uri_for { ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} ); carp "uri_for called with undef argument" if grep { ! defined $_ } @args; + + my @encoded_args = (); foreach my $arg (@args) { - utf8::encode($arg) if utf8::is_utf8($arg); - $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; + if(ref($arg)||'' eq 'ARRAY') { + push @encoded_args, [map { + my $encoded = encode_utf8 $_; + $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; + $encoded; + } @$arg]; + } else { + push @encoded_args, do { + my $encoded = encode_utf8 $arg; + $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; + $encoded; + } + } } + my $target_action = $path->$_isa('Catalyst::Action') ? $path : undef; if ( $path->$_isa('Catalyst::Action') ) { # action object - s|/|%2F|g for @args; + s|/|%2F|g for @encoded_args; my $captures = [ map { s|/|%2F|g; $_; } - ( scalar @args && ref $args[0] eq 'ARRAY' - ? @{ shift(@args) } + ( scalar @encoded_args && ref $encoded_args[0] eq 'ARRAY' + ? @{ shift(@encoded_args) } : ()) ]; - foreach my $capture (@$captures) { - utf8::encode($capture) if utf8::is_utf8($capture); - $capture =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; - } - my $action = $path; # ->uri_for( $action, \@captures_and_args, \%query_values? ) - if( !@args && $action->number_of_args ) { + if( !@encoded_args && $action->number_of_args ) { my $expanded_action = $c->dispatcher->expand_action( $action ); - my $num_captures = $expanded_action->number_of_captures; - unshift @args, splice @$captures, $num_captures; + unshift @encoded_args, splice @$captures, $num_captures; } $path = $c->dispatcher->uri_for_action($action, $captures); @@ -1437,25 +1507,37 @@ sub uri_for { $path = '/' if $path eq ''; } - unshift(@args, $path); + unshift(@encoded_args, $path); unless (defined $path && $path =~ s!^/!!) { # in-place strip my $namespace = $c->namespace; if (defined $path) { # cheesy hack to handle path '../foo' - $namespace =~ s{(?:^|/)[^/]+$}{} while $args[0] =~ s{^\.\./}{}; + $namespace =~ s{(?:^|/)[^/]+$}{} while $encoded_args[0] =~ s{^\.\./}{}; } - unshift(@args, $namespace || ''); + unshift(@encoded_args, $namespace || ''); } # join args with '/', or a blank string - my $args = join('/', grep { defined($_) } @args); + my $args = join('/', grep { defined($_) } @encoded_args); $args =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE $args =~ s!^/+!!; my ($base, $class) = ('/', 'URI::_generic'); if(blessed($c)) { $base = $c->req->base; - $class = ref($base); + if($target_action) { + $target_action = $c->dispatcher->expand_action($target_action); + if(my $s = $target_action->scheme) { + $s = lc($s); + $class = "URI::$s"; + $base->scheme($s); + } else { + $class = ref($base); + } + } else { + $class = ref($base); + } + $base =~ s{(?{$_}; - s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go; + #s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go; ## Commented out because seems to lead to double encoding - JNAP s/ /+/g; my $key = $_; $val = '' unless defined $val; (map { my $param = "$_"; - utf8::encode( $param ) if utf8::is_utf8($param); + $param = encode_utf8($param); # using the URI::Escape pattern here so utf8 chars survive $param =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go; $param =~ s/ /+/g; + + $key = encode_utf8($key); + # using the URI::Escape pattern here so utf8 chars survive + $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go; + $key =~ s/ /+/g; + "${key}=$param"; } ( ref $val eq 'ARRAY' ? @$val : $val )); } @keys); } @@ -2017,6 +2105,8 @@ sub finalize_headers { $c->finalize_cookies; + # This currently is a NOOP but I don't want to remove it since I guess people + # might have Response subclasses that use it for something... (JNAP) $c->response->finalize_headers(); # Done @@ -2025,42 +2115,49 @@ sub finalize_headers { =head2 $c->finalize_encoding -Make sure your headers and body are encoded properly IF you set an encoding. +Make sure your body is encoded properly IF you set an encoding. By +default the encoding is UTF-8 but you can disable it by explicitly setting the +encoding configuration value to undef. + +We can only encode when the body is a scalar. Methods for encoding via the +streaming interfaces (such as C and C on L +are available). + See L. =cut sub finalize_encoding { my $c = shift; - - my $body = $c->response->body; - - return unless defined($body); - - my $enc = $c->encoding; - - return unless $enc; - - my ($ct, $ct_enc) = $c->response->content_type; - - # Only touch 'text-like' contents - return unless $c->response->content_type =~ /^text|xml$|javascript$/; - - if ($ct_enc && $ct_enc =~ /charset=([^;]*)/) { - if (uc($1) ne uc($enc->mime_name)) { - $c->log->debug("Unicode::Encoding is set to encode in '" . - $enc->mime_name . - "', content type is '$1', not encoding "); - return; - } - } else { - $c->res->content_type($c->res->content_type . "; charset=" . $enc->mime_name); + my $res = $c->res || return; + + # Warn if the set charset is different from the one you put into encoding. We need + # to do this early since encodable_response is false for this condition and we need + # to match the debug output for backcompat (there's a test for this...) -JNAP + if( + $res->content_type_charset and $c->encoding and + (uc($c->encoding->mime_name) ne uc($res->content_type_charset)) + ) { + my $ct = lc($res->content_type_charset); + $c->log->debug("Catalyst encoding config is set to encode in '" . + $c->encoding->mime_name . + "', content type is '$ct', not encoding "); } - # Oh my, I wonder what filehandle responses and streams do... - jnap. - # Encode expects plain scalars (IV, NV or PV) and segfaults on ref's - $c->response->body( $c->encoding->encode( $body, $c->_encode_check ) ) - if ref(\$body) eq 'SCALAR'; + if( + ($res->encodable_response) and + (defined($res->body)) and + (ref(\$res->body) eq 'SCALAR') + ) { + $c->res->body( $c->encoding->encode( $c->res->body, $c->_encode_check ) ); + + # Set the charset if necessary. This might be a bit bonkers since encodable response + # is false when the set charset is not the same as the encoding mimetype (maybe + # confusing action at a distance here.. + # Don't try to set the charset if one already exists + $c->res->content_type($c->res->content_type . "; charset=" . $c->encoding->mime_name) + unless($c->res->content_type_charset); + } } =head2 $c->finalize_output @@ -2274,7 +2371,7 @@ Prepares body parameters. sub prepare_body_parameters { my $c = shift; - $c->engine->prepare_body_parameters( $c, @_ ); + $c->request->prepare_body_parameters( $c, @_ ); } =head2 $c->prepare_connection @@ -2368,6 +2465,10 @@ sub log_request { $method ||= ''; $path = '/' unless length $path; $address ||= ''; + + $path =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; + $path = decode_utf8($path); + $c->log->debug(qq/"$method" request for "$path" from "$address"/); $c->log_request_headers($request->headers); @@ -2553,37 +2654,6 @@ Prepares uploads. sub prepare_uploads { my $c = shift; $c->engine->prepare_uploads( $c, @_ ); - - my $enc = $c->encoding; - return unless $enc; - - # Uggg we hook prepare uploads to do the encoding crap on post and query - # parameters! Sorry -jnap - for my $key (qw/ parameters query_parameters body_parameters /) { - for my $value ( values %{ $c->request->{$key} } ) { - # N.B. Check if already a character string and if so do not try to double decode. - # http://www.mail-archive.com/catalyst@lists.scsys.co.uk/msg02350.html - # this avoids exception if we have already decoded content, and is _not_ the - # same as not encoding on output which is bad news (as it does the wrong thing - # for latin1 chars for example).. - $value = $c->_handle_unicode_decoding($value); - } - } - for my $value ( values %{ $c->request->uploads } ) { - # skip if it fails for uploads, as we don't usually want uploads touched - # in any way - for my $inner_value ( ref($value) eq 'ARRAY' ? @{$value} : $value ) { - $inner_value->{filename} = try { - $enc->decode( $inner_value->{filename}, $c->_encode_check ) - } catch { - $c->handle_unicode_encoding_exception({ - param_value => $inner_value->{filename}, - error_msg => $_, - encoding_step => 'uploads', - }); - }; - } - } } =head2 $c->prepare_write @@ -2958,15 +3028,30 @@ EOW Adds the following L middlewares to your application, since they are useful and commonly needed: -L, (conditionally added based on the status -of your $ENV{REMOTE_ADDR}, and can be forced on with C -or forced off with C), L -(if you are using Lighttpd), L (always -applied since this middleware is smart enough to conditionally apply itself). +L (if you are using Lighttpd), +L (always applied since this middleware +is smart enough to conditionally apply itself). + +We will also automatically add L if we notice +that your HTTP $env variable C is '127.0.0.1'. This is usually +an indication that your server is running behind a proxy frontend. However in +2014 this is often not the case. We preserve this code for backwards compatibility +however I B recommend that if you are running the server behind a front +end proxy that you clearly indicate so with the C configuration +setting to true for your environment configurations that run behind a proxy. This +way if you change your front end proxy address someday your code would inexplicably +stop working as expected. Additionally if we detect we are using Nginx, we add a bit of custom middleware to solve some problems with the way that server handles $ENV{PATH_INFO} and -$ENV{SCRIPT_NAME} +$ENV{SCRIPT_NAME}. + +Please B that if you do use C the middleware is now +adding via C rather than this method. + +If you are using Lighttpd or IIS6 you may wish to apply these middlewares. In +general this is no longer a common case but we have this here for backward +compatibility. =cut @@ -2974,16 +3059,21 @@ $ENV{SCRIPT_NAME} sub apply_default_middlewares { my ($app, $psgi_app) = @_; - $psgi_app = Plack::Middleware::Conditional->wrap( - $psgi_app, - builder => sub { Plack::Middleware::ReverseProxy->wrap($_[0]) }, - condition => sub { - my ($env) = @_; - return if $app->config->{ignore_frontend_proxy}; - return $env->{REMOTE_ADDR} eq '127.0.0.1' - || $app->config->{using_frontend_proxy}; - }, - ); + # Don't add this conditional IF we are explicitly saying we want the + # frontend proxy support. We don't need it here since if that is the + # case it will be always loaded in the default_middleware. + + unless($app->config->{using_frontend_proxy}) { + $psgi_app = Plack::Middleware::Conditional->wrap( + $psgi_app, + builder => sub { Plack::Middleware::ReverseProxy->wrap($_[0]) }, + condition => sub { + my ($env) = @_; + return if $app->config->{ignore_frontend_proxy}; + return $env->{REMOTE_ADDR} eq '127.0.0.1'; + }, + ); + } # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME # http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html @@ -3016,17 +3106,34 @@ sub apply_default_middlewares { return $psgi_app; } -=head2 $c->psgi_app +=head2 App->psgi_app + +=head2 App->to_app Returns a PSGI application code reference for the catalyst application -C<$c>. This is the bare application without any middlewares -applied. C<${myapp}.psgi> is not taken into account. +C<$c>. This is the bare application created without the C +method called. We do however apply C since those are +integral to how L functions. Also, unlike starting your application +with a generated server script (via L and C) we do +not attempt to return a valid L application using any existing C<${myapp}.psgi> +scripts in your $HOME directory. + +B C was originally created when the first PSGI +port was done for v5.90000. These are middlewares that are added to achieve +backward compatibility with older applications. If you start your application +using one of the supplied server scripts (generated with L and +the project skeleton script C) we apply C +automatically. This was done so that pre and post PSGI port applications would +work the same way. This is what you want to be using to retrieve the PSGI application code -reference of your Catalyst application for use in F<.psgi> files. +reference of your Catalyst application for use in a custom F<.psgi> or in your +own created server modules. =cut +*to_app = \&psgi_app; + sub psgi_app { my ($app) = @_; my $psgi = $app->engine->build_psgi_app($app); @@ -3063,8 +3170,14 @@ Sets up the input/output encoding. See L sub setup_encoding { my $c = shift; - my $enc = delete $c->config->{encoding}; - $c->encoding( $enc ) if defined $enc; + if( exists($c->config->{encoding}) && !defined($c->config->{encoding}) ) { + # Ok, so the user has explicitly said "I don't want encoding..." + return; + } else { + my $enc = defined($c->config->{encoding}) ? + delete $c->config->{encoding} : 'UTF-8'; # not sure why we delete it... (JNAP) + $c->encoding($enc); + } } =head2 handle_unicode_encoding_exception @@ -3102,8 +3215,13 @@ sub _handle_unicode_decoding { return $value; } elsif ( ref $value eq 'HASH' ) { - foreach ( values %$value ) { - $_ = $self->_handle_unicode_decoding($_); + foreach (keys %$value) { + my $encoded_key = $self->_handle_param_unicode_decoding($_); + $value->{$encoded_key} = $self->_handle_unicode_decoding($value->{$_}); + + # If the key was encoded we now have two (the original and current so + # delete the original. + delete $value->{$_} if $_ ne $encoded_key; } return $value; } @@ -3118,9 +3236,7 @@ sub _handle_param_unicode_decoding { my $enc = $self->encoding; return try { - Encode::is_utf8( $value ) ? - $value - : $enc->decode( $value, $self->_encode_check ); + $enc->decode( $value, $self->_encode_check ); } catch { $self->handle_unicode_encoding_exception({ @@ -3292,6 +3408,68 @@ the plugin name does not begin with C. } } +=head2 default_middleware + +Returns a list of instantiated PSGI middleware objects which is the default +middleware that is active for this application (taking any configuration +options into account, excluding your custom added middleware via the C +configuration option). You can override this method if you wish to change +the default middleware (although do so at risk since some middleware is vital +to application function.) + +The current default middleware list is: + + Catalyst::Middleware::Stash + Plack::Middleware::HTTPExceptions + Plack::Middleware::RemoveRedundantBody + Plack::Middleware::FixMissingBodyInRedirect + Plack::Middleware::ContentLength + Plack::Middleware::MethodOverride + Plack::Middleware::Head + +If the configuration setting C is true we add: + + Plack::Middleware::ReverseProxy + +If the configuration setting C is true we add: + + Plack::Middleware::ReverseProxyPath + +But B that L is not a dependency of the +L distribution so if you want to use this option you should add it to +your project distribution file. + +These middlewares will be added at L during the +L phase of application startup. + +=cut + +sub default_middleware { + my $class = shift; + my @mw = ( + Catalyst::Middleware::Stash->new, + Plack::Middleware::HTTPExceptions->new, + Plack::Middleware::RemoveRedundantBody->new, + Plack::Middleware::FixMissingBodyInRedirect->new, + Plack::Middleware::ContentLength->new, + Plack::Middleware::MethodOverride->new, + Plack::Middleware::Head->new); + + if($class->config->{using_frontend_proxy}) { + push @mw, Plack::Middleware::ReverseProxy->new; + } + + if($class->config->{using_frontend_proxy_path}) { + if(Class::Load::try_load_class('Plack::Middleware::ReverseProxyPath')) { + push @mw, Plack::Middleware::ReverseProxyPath->new; + } else { + $class->log->error("Cannot use configuration 'using_frontend_proxy_path' because 'Plack::Middleware::ReverseProxyPath' is not installed"); + } + } + + return @mw; +} + =head2 registered_middlewares Read only accessor that returns an array of all the middleware in the order @@ -3333,15 +3511,13 @@ up. sub registered_middlewares { my $class = shift; if(my $middleware = $class->_psgi_middleware) { - return ( - Catalyst::Middleware::Stash->new, - Plack::Middleware::HTTPExceptions->new, - Plack::Middleware::RemoveRedundantBody->new, - Plack::Middleware::FixMissingBodyInRedirect->new, - Plack::Middleware::ContentLength->new, - Plack::Middleware::MethodOverride->new, - Plack::Middleware::Head->new, - @$middleware); + my @mw = ($class->default_middleware, @$middleware); + + if($class->config->{using_frontend_proxy}) { + push @mw, Plack::Middleware::ReverseProxy->new; + } + + return @mw; } else { die "You cannot call ->registered_middlewares until middleware has been setup"; } @@ -3349,8 +3525,17 @@ sub registered_middlewares { sub setup_middleware { my $class = shift; - my @middleware_definitions = @_ ? - reverse(@_) : reverse(@{$class->config->{'psgi_middleware'}||[]}); + my @middleware_definitions; + + # If someone calls this method you can add middleware with args. However if its + # called without an arg we need to setup the configuration middleware. + if(@_) { + @middleware_definitions = reverse(@_); + } else { + @middleware_definitions = reverse(@{$class->config->{'psgi_middleware'}||[]}) + unless $class->finalized_default_middleware; + $class->finalized_default_middleware(1); # Only do this once, just in case some people call setup over and over... + } my @middleware = (); while(my $next = shift(@middleware_definitions)) { @@ -3447,7 +3632,7 @@ sub default_data_handlers { return eval { local $/; $slurped = $fh->getline; - $parser->can("decode_json")->($slurped); + $parser->can("decode_json")->($slurped); # decode_json does utf8 decoding for us } || Catalyst::Exception->throw(sprintf "Error Parsing POST '%s', Error: %s", (defined($slurped) ? $slurped : 'undef') ,$@); }, }; @@ -3644,8 +3829,20 @@ C - See L. =item * +C - Enabled L on your application (if +installed, otherwise log an error). This is useful if your application is not running on the +'root' (or /) of your host server. B if you use this feature you should add the required +middleware to your project dependency list since its not automatically a dependency of L. +This has been done since not all people need this feature and we wish to restrict the growth of +L dependencies. + +=item * + C - See L +This now defaults to 'UTF-8'. You my turn it off by setting this configuration +value to undef. + =item * C @@ -3988,8 +4185,45 @@ Please see L for more on middleware. =head1 ENCODING -On request, decodes all params from encoding into a sequence of -logical characters. On response, encodes body into encoding. +Starting in L version 5.90080 encoding is automatically enabled +and set to encode all body responses to UTF8 when possible and applicable. +Following is documentation on this process. If you are using an older +version of L you should review documentation for that version since +a lot has changed. + +By default encoding is now 'UTF-8'. You may turn it off by setting +the encoding configuration to undef. + + MyApp->config(encoding => undef); + +This is recommended for temporary backwards compatibility only. + +Encoding is automatically applied when the content-type is set to +a type that can be encoded. Currently we encode when the content type +matches the following regular expression: + + $content_type =~ /^text|xml$|javascript$/ + +Encoding is set on the application, but it is copied to the context object +so that you can override it on a request basis. + +Be default we don't automatically encode 'application/json' since the most +common approaches to generating this type of response (Either via L +or L) will do so already and we want to avoid double +encoding issues. + +If you are producing JSON response in an unconventional manner (such +as via a template or manual strings) you should perform the UTF8 encoding +manually as well such as to conform to the JSON specification. + +NOTE: We also examine the value of $c->response->content_encoding. If +you set this (like for example 'gzip', and manually gzipping the body) +we assume that you have done all the necessary encoding yourself, since +we cannot encode the gzipped contents. If you use a plugin like +L you need to update to a modern version in order +to have this function correctly with the new UTF8 encoding code, or you +can use L or (probably best) do your compression on +a front end proxy. =head2 Methods @@ -4220,9 +4454,11 @@ dd070: Dhaval Dhanani Upasana +John Napiorkowski (jnap) + =head1 COPYRIGHT -Copyright (c) 2005-2014, the above named PROJECT FOUNDER and CONTRIBUTORS. +Copyright (c) 2005-2015, the above named PROJECT FOUNDER and CONTRIBUTORS. =head1 LICENSE