view release on metacpan or search on metacpan
for applications.
- Various minor typo fixes.
New features:
- Allow passing additional arguments to action constructors.
5.80018 2010-01-12 22:24:20
Bug fixed:
- Call ->canonical on URI derived from $ENV{REQUEST_URI} to get
paths correctly decoded. This bug was previously hidden by a bug
in HTTP::Request::AsCGI.
Documentation:
- Clarify that uri_for_action works on private paths, with example.
- Clarify documentation about debug
Deprecations:
- Saying use Catalyst::Test; (without an application name or () to stop
the importer running is now deprecated and will issue a warning.
You should be saying use Catalyst::Test ();
- Fix reporting the wrong Content-Length if the response body is an
upgraded string. Strings mean the same thing whether or not they are
upgraded, may get upgraded even after they are encoded, and will
produce the same output either way, but bytes::length returns too big
values for upgraded strings containing characters >127
- Fix t/live_fork.t with bleadperl (RT#52100)
- Set $ENV{PATH_INFO} from $ENV{REQUEST_URI} combined with
$ENV{SCRIPT_NAME} if possible. This is many web servers always fully
decode PATH_INFO including URI reserved characters. This allows us to
tell foo%2cbar from foo%252cbar, and fixes issues with %2F in paths
being incorrectly decoded, resulting in too many path parts (rather
than 1 path part containing a /, on some web servers (at least nginx).
(RT#50082)
- Require new HTTP::Request::AsCGI so that it fully decodes $ENV{PATH_INFO}
in non CGI contexts. (RT#50082)
Refactoring / cleanups:
- NoTabs and Pod tests moved to t/author so that they're not run
(and then skipped) normally.
Documentation:
- Add Catalyst::Exception::Detach and ::Go, and refactor detach() and
go() to use them instead of magic, global strings.
Fixes RT#47366
- Clean up getting metaclass instance and making app class immutable
again in Catalyst::Test
5.80005 2009-06-06 14:40:00
Behaviour changes:
- Arguments ($c->req->args) in Chained dispatch are now automatically
URL decoded to be consistent with Local/Path dispatch
Documentation:
- Clarify correct techniques for Moose controllers (domm)
Bug fixes:
- Further change pushing 'env' attribute down into Catalyst::Engine
to make $c->engine->env work in all cases (kmx)
- Also fix $c->engine->env in Catalyst::Test tests (kmx)
- Tests for this
- Fix Catalyst failing to start if any plugin changed $_ whilst
- Fix for Path('0') handling (RT #29334)
- Workaround for Win32 and c3_mro.t (RT #26452, tested by Kenichi Ishigaki)
- Fix for encoding query parameters
- Fix Chained multiple test
5.7012 2007-12-16 23:44:00
- Fix uri_for()'s and uri_with()'s handling of multibyte chars
(Daisuke Murase)
- Fix __PACKAGE__->config->{foo} = 'bar' case with subclassing
- Add Catalyst::Stats (Jon Schutz)
- Fixed a bug where ?q=bar=baz is decoded as q=>'bar', not 'bar=baz'.
(Tatsuhiko Miyagawa, Masahiro Nagano)
- Fixed a bug where -rr (restart regex) command line option could cause
shell errors. (Aristotle Pagaltzis, Chisel Wright)
5.7011 2007-10-18 20:40:00
- Allow multiple restart directories and added option to follow
symlinks in the HTTP::Restarter engine (Sebastian Willert)
- Fixed t/optional_http-server-restart.t so it actually tests
if the server restarted or notified of an error (Sebastian Willert)
- Return child PID from the HTTP engine when run with the 'background' option.
(Emanuele Zeppieri)
- Fixed bug in HTTP engine where writes could fail with
'Resource temporarily unavailable'.
- Fixed bug where %2b in query parameter is doubly decoded to ' ', instead of '+'
(RT #30087, Gavin Henry, Tatsuhiko Miyagawa, Oleg Pronin)
- Fixed bug where req->base and req->uri would include a port number when running
in SSL mode.
- Removed unnecessary sprintf in debug mode that caused warnings on locales where
commas are used for decimal markers.
- Improved error message for case when server picks up editor save
files as module names. (James Mastros)
5.7010 2007-08-22 07:41:00
- Resource forks in 5.7009
lib/Catalyst.pm view on Meta::CPAN
}
}
=head2 handle_unicode_encoding_exception
Hook to let you customize how encoding errors are handled. By default
we just throw an exception and the default error page will pick it up.
Receives a hashref of debug information. Example of call (from the
Catalyst internals):
my $decoded_after_fail = $c->handle_unicode_encoding_exception({
param_value => $value,
error_msg => $_,
encoding_step => 'params',
});
The calling code expects to receive a decoded string or an exception.
You can override this for custom handling of unicode errors. By
default we just die. If you want a custom response here, one approach
is to throw an HTTP style exception, instead of returning a decoded
string or throwing a generic exception.
sub handle_unicode_encoding_exception {
my ($c, $params) = @_;
HTTP::Exception::BAD_REQUEST->throw(status_message=>$params->{error_msg});
}
Alternatively you can 'catch' the error, stash it and write handling code later
in your application:
lib/Catalyst.pm view on Meta::CPAN
=item use_request_uri_for_path => 0
This is the default (and the) traditional method that Catalyst has used for determining the path information.
The path is generated from a combination of the C<PATH_INFO> and C<SCRIPT_NAME> environment variables.
The allows the application to behave correctly when C<mod_rewrite> is being used to redirect requests
into the application, as these variables are adjusted by mod_rewrite to take account for the redirect.
However this method has the major disadvantage that it is impossible to correctly decode some elements
of the path, as RFC 3875 says: "C<< Unlike a URI path, the PATH_INFO is not URL-encoded, and cannot
contain path-segment parameters. >>" This means PATH_INFO is B<always> decoded, and therefore Catalyst
can't distinguish / vs %2F in paths (in addition to other encoded values).
=item use_request_uri_for_path => 1
This method uses the C<REQUEST_URI> and C<SCRIPT_NAME> environment variables. As C<REQUEST_URI> is never
decoded, this means that applications using this mode can correctly handle URIs including the %2F character
(i.e. with C<AllowEncodedSlashes> set to C<On> in Apache).
Given that this method of path resolution is provably more correct, it is recommended that you use
this unless you have a specific need to deploy your application in a non-standard environment, and you are
aware of the implications of not being able to handle encoded URI paths correctly.
However it also means that in a number of cases when the app isn't installed directly at a path, but instead
is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html directory, with mod_rewrite in a
.htaccess file, or when SSI is used to rewrite pages into the app, or when sub-paths of the app are exposed
at other URIs than that which the app is 'normally' based at with C<mod_rewrite>), the resolution of
lib/Catalyst.pm view on Meta::CPAN
The default method throws exceptions in the case of invalid request parameters
(resulting in a 500 error), but ignores errors in upload filenames.
The keys passed in the C<$exception_context> hash are:
=over
=item param_value
The value which was not able to be decoded.
=item error_msg
The exception received from L<Encode>.
=item encoding_step
What type of data was being decoded. Valid values are (currently)
C<params> - for request parameters / arguments / captures
and C<uploads> - for request upload filenames.
=back
=back
=head1 SUPPORT
IRC:
lib/Catalyst/Request/Upload.pm view on Meta::CPAN
Catalyst::Request::Upload - handles file upload requests
=head1 SYNOPSIS
my $upload = $c->req->upload('field');
$upload->basename;
$upload->copy_to;
$upload->fh;
$upload->decoded_fh
$upload->filename;
$upload->headers;
$upload->link_to;
$upload->size;
$upload->slurp;
$upload->decoded_slurp;
$upload->tempname;
$upload->type;
$upload->charset;
To specify where Catalyst should put the temporary files, set the 'uploadtmp'
option in the Catalyst config. If unset, Catalyst will use the system temp dir.
__PACKAGE__->config( uploadtmp => '/path/to/tmpdir' );
See also L<Catalyst>.
lib/Catalyst/Request/Upload.pm view on Meta::CPAN
}
return 0;
}
=head2 $upload->fh
Opens a temporary file (see tempname below) and returns an L<IO::File> handle.
This is a filehandle that is opened with no additional IO Layers.
=head2 $upload->decoded_fh(?$encoding)
Returns a filehandle that has binmode set to UTF-8 if a UTF-8 character set
is found. This also accepts an override encoding value that you can use to
force a particular L<PerlIO> layer. If neither are found the filehandle is
set to :raw.
This is useful if you are pulling the file into code and inspecting bits and
maybe then sending those bits back as the response. (Please note this is not
a suitable filehandle to set in the body; use C<fh> if you are doing that).
Please note that using this method sets the underlying filehandle IO layer
so once you use this method if you go back and use the C<fh> method you
still get the IO layer applied.
=cut
sub decoded_fh {
my ($self, $layer) = @_;
my $fh = $self->fh;
$layer = ':utf8_strict' if !$layer && $self->is_utf8_encoded;
$layer = ':raw' unless $layer;
binmode($fh, $layer);
return $fh;
}
lib/Catalyst/Request/Upload.pm view on Meta::CPAN
}
}
else {
$content = do { local $/; $handle->getline };
}
$handle->seek(0, IO::File::SEEK_SET);
return $content;
}
=head2 $upload->decoded_slurp(?$encoding)
Works just like C<slurp> except we use C<decoded_fh> instead of C<fh> to
open a filehandle to slurp. This means if your upload charset is UTF8
we binmode the filehandle to that encoding.
=cut
sub decoded_slurp {
my ( $self, $layer ) = @_;
my $handle = $self->decoded_fh($layer);
$handle->seek(0, IO::File::SEEK_SET);
my $content = do { local $/; $handle->getline };
$handle->seek(0, IO::File::SEEK_SET);
return $content;
}
=head2 $upload->basename
lib/Catalyst/Response.pm view on Meta::CPAN
Sets or returns the output (text or binary data). If you are returning a large body,
you might want to use a L<IO::Handle> type of object (Something that implements the getline method
in the same fashion), or a filehandle GLOB. These will be passed down to the PSGI
handler you are using and might be optimized using server specific abilities (for
example L<Twiggy> will attempt to server a real local file in a non blocking manner).
If you are using a filehandle as the body response you are responsible for
making sure it conforms to the L<PSGI> specification with regards to content
encoding. Unlike with scalar body values or when using the streaming interfaces
we currently do not attempt to normalize and encode your filehandle. In general
this means you should be sure to be sending bytes not UTF8 decoded multibyte
characters.
Most of the time when you do:
open(my $fh, '<:raw', $path);
You should be fine. If you open a filehandle with a L<PerlIO> layer you probably
are not fine. You can usually fix this by explicitly using binmode to set
the IOLayer to :raw. Its possible future versions of L<Catalyst> will try to
'do the right thing'.
lib/Catalyst/UTF8.pod view on Meta::CPAN
logs or in a response.)
[debug] Query Parameters are:
.-------------------------------------+--------------------------------------.
| Parameter | Value |
+-------------------------------------+--------------------------------------+
| ⥠| â¥â¥ |
'-------------------------------------+--------------------------------------'
All the values and keys that are part of $c->req->query_parameters will be
utf8 decoded. So you should not need to do anything special to take those
values/keys and send them to the body response (since as we will see later
L<Catalyst> will do all the necessary encoding for you).
Again, remember that values of your parameters are now decode into Unicode strings. so
for example you'd expect the result of length to reflect the character length not
the byte length.
Just like with arguments and captures, you can use utf8 literals (or utf8
strings) in $c->uri_for:
t/aggregate/catalyst_test_utf8.t view on Meta::CPAN
binmode Test::More->builder->failure_output, ":utf8";
use Catalyst::Test 'TestAppEncoding';
plan skip_all => 'This test does not run live'
if $ENV{CATALYST_SERVER};
{
# Test for https://rt.cpan.org/Ticket/Display.html?id=53678
# Catalyst::Test::get currently returns the raw octets, but it
# would be more useful if it decoded the content based on the
# Content-Type charset, as Test::WWW::Mechanize::Catalyst does
use utf8;
my $body = get('/utf8_non_ascii_content');
utf8::decode($body);
is $body, 'ÊsÊlÉÊÉÉ', 'Catalyst::Test::get returned content correctly UTF-8 encoded';
}
done_testing;
t/aggregate/live_component_controller_action_chained.t view on Meta::CPAN
local $TODO = 'gbjk never got off his ass and fixed this';
is( $response->header('X-Catalyst-Executed'),
$expected, 'Executed actions' );
}
}
{
ok( my $content =
get('http://localhost/chained/capture%2Farg%3B/return_arg/foo%2Fbar%3B'),
'request with URI-encoded arg' );
like( $content, qr{foo/bar;\z}, 'args decoded' );
like( $content, qr{capture/arg;}, 'captureargs decoded' );
}
{
ok( my $content =
get('http://localhost/chained/return_arg_decoded/foo%2Fbar%3B'),
'request with URI-encoded arg' );
like( $content, qr{foo/bar;\z}, 'args decoded' );
}
# Test round tripping, specifically the / character %2F in uri_for:
# not being able to feed it back action + captureargs and args into uri for
# and result in the original request uri is a major piece of suck ;)
foreach my $thing (
['foo', 'bar'],
['foo%2Fbar', 'baz'],
['foo', 'bar%2Fbaz'],
['foo%2Fbar', 'baz%2Fquux'],
t/aggregate/live_component_controller_action_local.t view on Meta::CPAN
else {
is_deeply $request->arguments, ['foo/bar'], "Parameters don't split on %2F";
}
}
}
{
ok( my $content = get('http://locahost/action/local/five/foo%2Fbar%3B'),
'request with URI-encoded arg');
# this is the CURRENT behavior
like( $content, qr{'foo/bar;'}, 'args for Local actions URI-decoded' );
}
}
t/aggregate/live_engine_request_parameters.t view on Meta::CPAN
is_deeply( $creq->parameters, $parameters,
'Catalyst::Request parameters' );
}
{
my $creq;
ok( my $response = request("http://localhost/dump/request?q=foo%2bbar"),
'Request' );
ok( $response->is_success, 'Response Successful 2xx' );
is( $response->content_type, 'text/plain', 'Response Content-Type' );
ok( eval '$creq = ' . $response->content );
is $creq->parameters->{q}, 'foo+bar', '%2b not double decoded';
}
{
my $creq;
ok( my $response = request("http://localhost/dump/request?q=foo=bar"),
'Request' );
ok( $response->is_success, 'Response Successful 2xx' );
is( $response->content_type, 'text/plain', 'Response Content-Type' );
ok( eval '$creq = ' . $response->content );
is $creq->parameters->{q}, 'foo=bar', '= not ignored';
t/lib/TestApp/Controller/Action/Chained.pm view on Meta::CPAN
sub capture_end : Chained('wurst') Args(0) PathPart('') { }
# */search vs doc/*
sub view : Chained('/') PathPart('chained') CaptureArgs(1) {}
sub star_search : Chained('view') PathPart('search') Args(0) { }
sub doc_star : Chained('/') PathPart('chained/doc') Args(1) {}
sub return_arg : Chained('view') PathPart('return_arg') Args(1) {}
sub return_arg_decoded : Chained('/') PathPart('chained/return_arg_decoded') Args(1) {
my ($self, $c) = @_;
$c->req->args([ map { decode_entities($_) } @{ $c->req->args }]);
}
sub roundtrip_urifor : Chained('/') PathPart('chained/roundtrip_urifor') CaptureArgs(1) {}
sub roundtrip_urifor_end : Chained('roundtrip_urifor') PathPart('') Args(1) {
my ($self, $c) = @_;
# This should round-trip, always - i.e. the uri you put in should come back out.
$c->res->body($c->uri_for($c->action, $c->req->captures, @{$c->req->args}, $c->req->parameters));
$c->stash->{no_end} = 1;
t/lib/TestAppUnicode/Controller/Root.pm view on Meta::CPAN
my $data = "ã»ã"; # hoge!
utf8::encode($data);
$c->response->body($data);
$c->res->content_type('text/plain');
$c->encoding(undef);
}
sub unicode :Local {
my ($self, $c) = @_;
my $data = "ã»ã"; # hoge!
$c->response->body($data); # should be decoded
$c->res->content_type('text/plain');
}
sub not_unicode :Local {
my ($self, $c) = @_;
my $data = "\x{1234}\x{5678}";
utf8::encode($data); # DO NOT WANT unicode
$c->response->body($data); # just some octets
$c->res->content_type('text/plain');
$c->encoding(undef);
t/lib/TestAppUnicode/Controller/Root.pm view on Meta::CPAN
sub capture_charset : Chained('/') Args(1) {
my ( $self, $c, $cap_arg ) = @_;
$c->stash(charset => $cap_arg);
$c->forward('main');
}
sub shift_jis :Local {
my ($self, $c) = @_;
my $data = "ã»ã"; # hoge!
$c->response->body($data); # should be decoded
$c->res->content_type('text/plain; charset=Shift_JIS');
$c->encoding("Shift_JIS");
}
1;
t/unicode_plugin_live.t view on Meta::CPAN
# setup library path
use FindBin qw($Bin);
use lib "$Bin/lib";
use Catalyst::Test qw(TestAppUnicode);
{
my $res = request('/');
ok($res->is_success, 'get main page');
like($res->decoded_content, qr/it works/i, 'see if it has our text');
is ($res->header('Content-Type'), 'text/html; charset=UTF-8',
'Content-Type with charset'
);
}
{
my $res = request('/unicode_no_enc');
ok($res->is_success, 'get unicode_no_enc');
my $exp = "\xE3\x81\xBB\xE3\x81\x92";
my $got = Encode::encode_utf8($res->decoded_content);
is ($res->header('Content-Type'), 'text/plain',
'Content-Type with no charset');
is($got, $exp, 'content contains hoge');
}
{
my $res = request('/unicode');
ok( $res->is_success, 'get unicode');
is ($res->header('Content-Type'), 'text/plain; charset=UTF-8',
'Content-Type with charset');
my $exp = "\xE3\x81\xBB\xE3\x81\x92";
my $got = Encode::encode_utf8($res->decoded_content);
is($got, $exp, 'content contains hoge');
}
{
my $res = request('/not_unicode');
ok($res->is_success, 'get bytes');
my $exp = "\xE1\x88\xB4\xE5\x99\xB8";
my $got = Encode::encode_utf8($res->decoded_content);
is($got, $exp, 'got 1234 5678');
}
{
my $res = request('/file');
ok($res->is_success, 'get file');
like($res->decoded_content, qr/this is a test/, 'got filehandle contents');
}
{
# The latin 1 case is the one everyone forgets. I want to really make sure
# its right, so lets check the damn bytes.
my $res = request('/latin1');
ok($res->is_success, 'get latin1');
is ($res->header('Content-Type'), 'text/plain; charset=UTF-8',
'Content-Type with charset');
my $exp = "LATIN SMALL LETTER E WITH ACUTE: \xC3\xA9";
my $got = Encode::encode_utf8($res->decoded_content);
is ($got, $exp, 'content octets are UTF-8');
}
{
my $res = request('/shift_jis');
ok($res->is_success, 'get shift_jis');
is ($res->header('Content-Type'), 'text/plain; charset=Shift_JIS', 'Content-Type with charset');
my $exp = "\xE3\x81\xBB\xE3\x81\x92";
my $got = Encode::encode_utf8($res->decoded_content);
is ($got, $exp, 'content octets are Shift_JIS');
}
done_testing;
t/unicode_plugin_no_encoding.t view on Meta::CPAN
use URI::Escape qw/uri_escape_utf8/;
use HTTP::Status 'is_server_error';
use Data::Dumper;
my $encode_str = "\x{e3}\x{81}\x{82}"; # e38182 is japanese 'ã'
my $decode_str = Encode::decode('utf-8' => $encode_str);
my $escape_str = uri_escape_utf8($decode_str);
# JNAP - I am removing this test case because I think its not correct. I think
# we do not check the server encoding to determine if the parts of a request URL
# both paths and query should be decoded. I think its always safe to assume utf8
# encoded urlencoded bits. That is my reading of the spec. Please correct me if
# I am wrong
#check_parameter(GET "/?myparam=$escape_str");
check_parameter(POST '/',
Content_Type => 'form-data',
Content => [
'myparam' => [
"$Bin/unicode_plugin_no_encoding.t",
"$Bin/unicode_plugin_request_decode.t",
]
t/utf_incoming.t view on Meta::CPAN
sub file_upload :POST Consumes(Multipart) Local {
my ($self, $c) = @_;
Test::More::is $c->req->body_parameters->{'â¥'}, 'â¥â¥';
Test::More::ok my $upload = $c->req->uploads->{file};
Test::More::is $upload->charset, 'UTF-8';
my $text = $upload->slurp;
Test::More::is Encode::decode_utf8($text), "<p>This is stream_body_fh action â¥</p>\n";
my $decoded_text = $upload->decoded_slurp;
Test::More::is $decoded_text, "<p>This is stream_body_fh action â¥</p>\n";
Test::More::is $upload->filename, 'â¥ttachment.txt';
Test::More::is $upload->raw_basename, 'â¥ttachment.txt';
$c->response->content_type('text/html');
$c->response->body($decoded_text);
}
sub file_upload_utf8_param :POST Consumes(Multipart) Local {
my ($self, $c) = @_;
Test::More::is $c->req->body_parameters->{'â¥'}, 'â¥â¥';
Test::More::ok my $upload = $c->req->uploads->{'â¥'};
Test::More::is $upload->charset, 'UTF-8';
my $text = $upload->slurp;
Test::More::is Encode::decode_utf8($text), "<p>This is stream_body_fh action â¥</p>\n";
my $decoded_text = $upload->decoded_slurp;
Test::More::is $decoded_text, "<p>This is stream_body_fh action â¥</p>\n";
Test::More::is $upload->filename, 'â¥ttachment.txt';
Test::More::is $upload->raw_basename, 'â¥ttachment.txt';
$c->response->content_type('text/html');
$c->response->body($decoded_text);
}
sub json :POST Consumes(JSON) Local {
my ($self, $c) = @_;
my $post = $c->req->body_data;
Test::More::is $post->{'â¥'}, 'â¥â¥';
Test::More::is length($post->{'â¥'}), 2;
$c->response->content_type('application/json');
t/utf_incoming.t view on Meta::CPAN
is decode_utf8($res->content), "<p>This is stream_body_fh action â¥</p>\n";
}
{
ok my $req = POST '/root/json',
Content_Type => 'application/json',
Content => encode_json +{'â¥'=>'â¥â¥'}; # Note: JSON does the UTF* encoding for us
ok my $res = request $req;
## decode_json expect the binary utf8 string and does the decoded bit for us.
is_deeply decode_json(($res->content)), +{'â¥'=>'â¥â¥'}, 'JSON was decoded correctly';
}
{
ok my $res = request "/root/override_encoding";
ok my $enc = Encode::find_encoding('SHIFT_JIS');
is $res->code, 200, 'OK';
is $enc->decode($res->content), "ãã¹ã", 'correct body';
is $res->content_length, 6, 'correct length'; # Bytes over the wire
is length($enc->decode($res->content)), 3;
t/utf_incoming.t view on Meta::CPAN
arg2 => [
undef, '',
'Content-Type' =>'text/plain; charset=SHIFT_JIS',
'Content' => Encode::encode('SHIFT_JIS', $shiftjs)],
];
my ($res, $c) = ctx_request $req;
is $c->req->body_parameters->{'arg0'}, 'helloworld', 'got helloworld value';
is $c->req->body_parameters->{'â¥'}, 'â¥â¥';
is $c->req->body_parameters->{'arg1'}, $utf8, 'decoded utf8 param';
is $c->req->body_parameters->{'arg2'}[0], $shiftjs, 'decoded shiftjs param';
is $c->req->body_parameters->{'arg2'}[1], $shiftjs, 'decoded shiftjs param';
is $c->req->body_parameters->{'â¥â¥â¥'}, $shiftjs, 'decoded shiftjs param';
}
{
my $shiftjs = 'test ãã¹ã';
my $encoded = Encode::encode('UTF-8', $shiftjs);
ok my $req = GET "/root/echo_arg?a=$encoded";
my ($res, $c) = ctx_request $req;
t/utf_incoming.t view on Meta::CPAN
{
my $req = POST "/root/echo_arg", Content => "arg0=$invalid";
my $res = request $req;
is ($res->code, '400', "Invalid post param is 400");
}
# in query
{
# failing since 5.90080
my $req = GET "/root/echo_param?arg=$invalid";
my $res = request $req;
is ($res->code, '400', "Invalid get param is 400") or diag Dumper($res->decoded_content);
}
}
## should we use binmode on filehandles to force the encoding...?
## Not sure what else to do with multipart here, if docs are enough...
done_testing;