diff --git a/lib/Mojo/Message.pm b/lib/Mojo/Message.pm index 7750483598..ff0556d18d 100644 --- a/lib/Mojo/Message.pm +++ b/lib/Mojo/Message.pm @@ -9,7 +9,7 @@ use Mojo::JSON qw(j); use Mojo::JSON::Pointer; use Mojo::Parameters; use Mojo::Upload; -use Mojo::Util qw(decode); +use Mojo::Util qw(encode decode); has content => sub { Mojo::Content::Single->new }; has default_charset => 'UTF-8'; @@ -132,7 +132,8 @@ sub is_limit_exceeded { !!shift->{limit} } sub json { my ($self, $pointer) = @_; return undef if $self->content->is_multipart; - my $data = $self->{json} //= j($self->body); + my $charset = $self->body_params->charset || $self->default_charset; + my $data = $self->{json} //= j($charset eq 'UTF-8' ? $self->body : encode('UTF-8', decode($charset, $self->body))); return $pointer ? Mojo::JSON::Pointer->new($data)->get($pointer) : $data; } diff --git a/t/mojo/response.t b/t/mojo/response.t index e44bcde360..030f246ca7 100644 --- a/t/mojo/response.t +++ b/t/mojo/response.t @@ -5,9 +5,10 @@ use Mojo::Asset::File; use Mojo::Content::Single; use Mojo::Content::MultiPart; use Mojo::File qw(tempdir); -use Mojo::JSON qw(encode_json); +use Mojo::JSON qw(encode_json to_json); use Mojo::Message::Response; -use Mojo::Util qw(encode gzip); +use Mojo::Util qw(encode gzip); +use Mojo::ByteStream qw(b); subtest 'Defaults' => sub { my $res = Mojo::Message::Response->new; @@ -1074,6 +1075,62 @@ subtest 'Parse response and extract JSON data' => sub { is_deeply $res->json('/baz'), [1, 4, 3], 'right result'; }; +subtest 'Parse response and extract JSON data in shift_jis encoding' => sub { + my $yatta = 'やった'; + my $yatta_sjis = b($yatta)->encode('shift_jis')->to_string; + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0a"); + $res->parse("Content-Type: application/json;charset=shift_jis\x0a"); + $res->parse("Content-Length: 16\x0a\x0a"); + $res->parse(to_json({foo => $yatta_sjis})); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is length($res->body), 16, 'right length'; + is $res->body_params->charset, "shift_jis", 'right charset'; + is_deeply $res->json, {foo => $yatta}, 'right JSON data'; + is $res->json('/foo'), $yatta, 'right result'; +}; + +subtest 'Parse response and extract JSON data in euc-jp encoding' => sub { + my $miyagawa = '宮川'; + my $miyagawa_jp = b($miyagawa)->encode('euc-jp')->to_string; + is $miyagawa_jp, "\x{B5}\x{DC}\x{C0}\x{EE}", 'correct encoding'; + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0a"); + $res->parse("Content-Type: application/json;charset=euc-jp\x0a"); + $res->parse("Content-Length: 14\x0a\x0a"); + $res->parse(to_json({foo => $miyagawa_jp})); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is length($res->body), 14, 'right length'; + is $res->body_params->charset, "euc-jp", 'right charset'; + is_deeply $res->json, {foo => $miyagawa}, 'right JSON data'; + is $res->json('/foo'), $miyagawa, 'right result'; +}; + +subtest 'Parse response and extract JSON data in ISO-8859-1 encoding' => sub { + my $hola = "áèñ"; + my $hola_latin1 = b($hola)->encode('ISO-8859-1')->to_string; + is $hola_latin1, "\x{e1}\x{e8}\x{f1}", 'correct encoding'; + my $res = Mojo::Message::Response->new; + $res->parse("HTTP/1.1 200 OK\x0a"); + $res->parse("Content-Type: application/json;charset=ISO-8859-1\x0a"); + $res->parse("Content-Length: 13\x0a\x0a"); + $res->parse(to_json({foo => $hola_latin1})); + ok $res->is_finished, 'response is finished'; + is $res->code, 200, 'right status'; + is $res->message, 'OK', 'right message'; + is $res->version, '1.1', 'right version'; + is length($res->body), 13, 'right length'; + is $res->body_params->charset, "ISO-8859-1", 'right charset'; + is_deeply $res->json, {foo => $hola}, 'right JSON data'; + is $res->json('/foo'), $hola, 'right result'; +}; + subtest 'Parse response and extract HTML' => sub { my $res = Mojo::Message::Response->new; $res->parse("HTTP/1.1 200 OK\x0a"); diff --git a/t/mojolicious/json_charset_lite_app.t b/t/mojolicious/json_charset_lite_app.t new file mode 100644 index 0000000000..4293e0e5f1 --- /dev/null +++ b/t/mojolicious/json_charset_lite_app.t @@ -0,0 +1,63 @@ +use Mojo::Base -strict; + +BEGIN {$ENV{MOJO_REACTOR} = 'Mojo::Reactor::Poll'} + +use Test::Mojo; +use Test::More; +use Mojo::ByteStream qw(b); +use Mojolicious::Lite; + +my $ascii = 'abc'; +my $yatta = 'やった'; +my $yatta_sjis = b($yatta)->encode('shift_jis')->to_string; +my $miyagawa = '宮川'; +my $miyagawa_jp = b($miyagawa)->encode('euc-jp')->to_string; +my $hola = "áèñ"; +my $hola_latin1 = "\x{e1}\x{e8}\x{f1}"; + +get '/' => [ format => [ $yatta ] ] => { format => undef } => 'index'; + +post '/' => sub { + my $c = shift; + $c->render(text => "foo: " . $c->param('foo')); +}; + +get '/ascii' => sub { + my $c = shift; + $c->render(json => { test => $ascii }) +}; + +get '/unicode' => sub { + my $c = shift; + $c->render(json => { test => $yatta }) +}; + +get '/shift_jis' => sub { + my $c = shift; + $c->res->headers->content_type("application/json;charset=Shift_JIS"); + $c->render(data => qq({"test":"$yatta_sjis"})); +}; + +get '/euc_jp' => sub { + my $c = shift; + $c->res->headers->content_type("application/json;charset=euc-jp"); + $c->render(data => qq({"test":"$miyagawa_jp"})); +}; + +get '/latin1' => sub { + my $c = shift; + $c->res->headers->content_type("application/json;charset=ISO-8859-1"); + $c->stash(data => qq({"test":"$hola_latin1"})); +}; + +my $t = Test::Mojo->new; + +$t->get_ok('/ascii')->status_is(200)->content_is('{"test":"abc"}'); +$t->get_ok('/ascii')->status_is(200)->json_is('/test' => $ascii); +$t->get_ok('/unicode')->status_is(200)->content_type_is('application/json;charset=UTF-8')->json_is('/test' => $yatta); +$t->get_ok('/shift_jis')->status_is(200)->content_type_is('application/json;charset=Shift_JIS')->json_is('/test' => $yatta); +$t->get_ok('/euc_jp')->status_is(200)->content_type_is('application/json;charset=euc-jp')->json_is('/test' => $miyagawa); +$t->get_ok('/latin1')->status_is(200)->content_type_is('application/json;charset=ISO-8859-1')->json_is('/test' => $hola); + + +done_testing();