summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDylan William Hardison <dylan@hardison.net>2018-10-13 06:32:57 +0200
committerGitHub <noreply@github.com>2018-10-13 06:32:57 +0200
commit5688d0e712b85bc892ce405a1b79e3571f6d6d0f (patch)
tree39277970bd3648781fbecd1ebdcc9b3d6693d4b9
parent9263b7453169816c23b6f307fd225f3676d57a3a (diff)
downloadbugzilla-5688d0e712b85bc892ce405a1b79e3571f6d6d0f.tar.gz
bugzilla-5688d0e712b85bc892ce405a1b79e3571f6d6d0f.tar.xz
Bug 1495741 - memory issues: Avoid copying stuff in the webservice layer so much
-rw-r--r--Bugzilla/WebService/JSON.pm64
-rw-r--r--Bugzilla/WebService/JSON/Box.pm43
-rw-r--r--Bugzilla/WebService/Server/JSONRPC.pm14
-rw-r--r--Bugzilla/WebService/Server/REST.pm1
-rw-r--r--t/json-boxes.t36
5 files changed, 156 insertions, 2 deletions
diff --git a/Bugzilla/WebService/JSON.pm b/Bugzilla/WebService/JSON.pm
new file mode 100644
index 000000000..5c28b20f4
--- /dev/null
+++ b/Bugzilla/WebService/JSON.pm
@@ -0,0 +1,64 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::WebService::JSON;
+use 5.10.1;
+use Moo;
+
+use Bugzilla::Logging;
+use Bugzilla::WebService::JSON::Box;
+use JSON::MaybeXS;
+use Scalar::Util qw(refaddr blessed);
+use Package::Stash;
+
+use constant Box => 'Bugzilla::WebService::JSON::Box';
+
+has 'json' => (
+ init_arg => undef,
+ is => 'lazy',
+ handles => {_encode => 'encode', _decode => 'decode'},
+);
+
+sub encode {
+ my ($self, $value) = @_;
+ return Box->new(json => $self, value => $value);
+}
+
+sub decode {
+ my ($self, $box) = @_;
+
+ if (blessed($box) && $box->isa(Box)) {
+ return $box->value;
+ }
+ else {
+ return $self->_decode($box);
+ }
+}
+
+sub _build_json { JSON::MaybeXS->new }
+
+# delegation all the json options to the real json encoder.
+{
+ my @json_methods = qw(
+ utf8 ascii pretty canonical
+ allow_nonref allow_blessed convert_blessed
+ );
+ my $stash = Package::Stash->new(__PACKAGE__);
+ foreach my $method (@json_methods) {
+ my $symbol = '&' . $method;
+ $stash->add_symbol(
+ $symbol => sub {
+ my $self = shift;
+ $self->json->$method(@_);
+ return $self;
+ }
+ );
+ }
+}
+
+
+1;
diff --git a/Bugzilla/WebService/JSON/Box.pm b/Bugzilla/WebService/JSON/Box.pm
new file mode 100644
index 000000000..fc39aeee8
--- /dev/null
+++ b/Bugzilla/WebService/JSON/Box.pm
@@ -0,0 +1,43 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+
+package Bugzilla::WebService::JSON::Box;
+use 5.10.1;
+use Moo;
+
+use overload '${}' => 'value', '""' => 'to_string', fallback => 1;
+
+has 'value' => (is => 'ro', required => 1);
+has 'json' => (is => 'ro', required => 1);
+has 'label' => (is => 'lazy');
+has 'encode' => (init_arg => undef, is => 'lazy', predicate => 'is_encoded');
+
+sub TO_JSON {
+ my ($self) = @_;
+
+ return $self->to_string;
+}
+
+sub to_string {
+ my ($self) = @_;
+
+ return $self->is_encoded ? $self->encode : $self->label;
+}
+
+sub _build_encode {
+ my ($self) = @_;
+
+ return $self->json->_encode($self->value);
+}
+
+sub _build_label {
+ my ($self) = @_;
+
+ return "" . $self->value;
+}
+
+1;
diff --git a/Bugzilla/WebService/Server/JSONRPC.pm b/Bugzilla/WebService/Server/JSONRPC.pm
index 093167048..12a3143cc 100644
--- a/Bugzilla/WebService/Server/JSONRPC.pm
+++ b/Bugzilla/WebService/Server/JSONRPC.pm
@@ -31,7 +31,9 @@ use Bugzilla::Util;
use HTTP::Message;
use MIME::Base64 qw(decode_base64 encode_base64);
+use Scalar::Util qw(blessed);
use List::MoreUtils qw(none);
+use Bugzilla::WebService::JSON;
#####################################
# Public JSON::RPC Method Overrides #
@@ -48,7 +50,7 @@ sub new {
sub create_json_coder {
my $self = shift;
- my $json = $self->SUPER::create_json_coder(@_);
+ my $json = Bugzilla::WebService::JSON->new;
$json->allow_blessed(1);
$json->convert_blessed(1);
$json->allow_nonref(1);
@@ -83,6 +85,9 @@ sub response {
# Implement JSONP.
if (my $callback = $self->_bz_callback) {
my $content = $response->content;
+ if (blessed $content) {
+ $content = $content->encode;
+ }
# Prepend the JSONP response with /**/ in order to protect
# against possible encoding attacks (e.g., affecting Flash).
$response->content("/**/$callback($content)");
@@ -110,7 +115,12 @@ sub response {
else {
push(@header_args, "-ETag", $etag) if $etag;
print $cgi->header(-status => $response->code, @header_args);
- print $response->content;
+ my $content = $response->content;
+ if (blessed $content) {
+ $content = $content->encode;
+ utf8::encode($content);
+ }
+ print $content;
}
}
diff --git a/Bugzilla/WebService/Server/REST.pm b/Bugzilla/WebService/Server/REST.pm
index 13896b248..5d8367410 100644
--- a/Bugzilla/WebService/Server/REST.pm
+++ b/Bugzilla/WebService/Server/REST.pm
@@ -165,6 +165,7 @@ sub response {
my $template = Bugzilla->template;
$content = "";
+ $result->encode if blessed $result;
$template->process("rest.html.tmpl", { result => $result }, \$content)
|| ThrowTemplateError($template->error());
diff --git a/t/json-boxes.t b/t/json-boxes.t
new file mode 100644
index 000000000..4d9816e83
--- /dev/null
+++ b/t/json-boxes.t
@@ -0,0 +1,36 @@
+#!/usr/bin/perl
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This Source Code Form is "Incompatible With Secondary Licenses", as
+# defined by the Mozilla Public License, v. 2.0.
+use strict;
+use warnings;
+use 5.10.1;
+use lib qw( . lib local/lib/perl5 );
+
+use Scalar::Util qw(weaken);
+use Mojo::JSON qw(encode_json);
+use Scalar::Util qw(refaddr);
+use Test2::V0;
+
+use ok 'Bugzilla::WebService::JSON';
+
+my $json = Bugzilla::WebService::JSON->new;
+my $ref = {foo => 1};
+is(refaddr $json->decode($json->encode($ref)), refaddr $ref);
+
+my $box = $json->encode($ref);
+
+is($json->decode(q[{"foo":1}]), {foo => 1});
+is($json->decode($box), {foo => 1});
+
+is "$box", $box->label;
+
+$box->encode;
+
+is encode_json([ $box ]), encode_json([ encode_json($box->value) ]);
+is "$box", q[{"foo":1}];
+
+done_testing;