]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 1495741 - memory issues: Avoid copying stuff in the webservice layer so much
authorDylan William Hardison <dylan@hardison.net>
Sat, 13 Oct 2018 04:32:57 +0000 (00:32 -0400)
committerGitHub <noreply@github.com>
Sat, 13 Oct 2018 04:32:57 +0000 (00:32 -0400)
Bugzilla/WebService/JSON.pm [new file with mode: 0644]
Bugzilla/WebService/JSON/Box.pm [new file with mode: 0644]
Bugzilla/WebService/Server/JSONRPC.pm
Bugzilla/WebService/Server/REST.pm
t/json-boxes.t [new file with mode: 0644]

diff --git a/Bugzilla/WebService/JSON.pm b/Bugzilla/WebService/JSON.pm
new file mode 100644 (file)
index 0000000..5c28b20
--- /dev/null
@@ -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 (file)
index 0000000..fc39aee
--- /dev/null
@@ -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;
index 0931670487557720c91fb9f152b199dd0a636136..12a3143cc51f265fad5285e2f51e9fdcc208e43d 100644 (file)
@@ -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;
     }
 }
 
index 13896b248098e5ab17ae2934ad1247e91631d781..5d8367410bc5f002181a73a4c2f6e1451790e021 100644 (file)
@@ -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 (file)
index 0000000..4d9816e
--- /dev/null
@@ -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;