]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 550727: Add JSONP Support to the JSON-RPC WebService Interface
authorMax Kanat-Alexander <mkanat@bugzilla.org>
Tue, 27 Apr 2010 19:14:36 +0000 (12:14 -0700)
committerMax Kanat-Alexander <mkanat@bugzilla.org>
Tue, 27 Apr 2010 19:14:36 +0000 (12:14 -0700)
r=dkl, a=mkanat

Bugzilla/WebService/Server/JSONRPC.pm
template/en/default/global/user-error.html.tmpl

index ae479ff5e3a59effccc2b220004526a08deac196..5ab5e4a7b79885b9a8f04176915dd515982596c7 100644 (file)
@@ -28,7 +28,7 @@ use Bugzilla::Error;
 use Bugzilla::WebService::Constants;
 use Bugzilla::WebService::Util qw(taint_data);
 
-use Bugzilla::Util qw(correct_urlbase);
+use Bugzilla::Util qw(correct_urlbase trim);
 
 #####################################
 # Public JSON::RPC Method Overrides #
@@ -58,11 +58,19 @@ sub create_json_coder {
 # Override the JSON::RPC method to return our CGI object instead of theirs.
 sub cgi { return Bugzilla->cgi; }
 
-# Override the JSON::RPC method to use $cgi->header properly instead of
-# just printing text directly. This fixes various problems, including
-# sending Bugzilla's cookies properly.
 sub response {
     my ($self, $response) = @_;
+
+    # Implement JSONP.
+    if (my $callback = $self->_bz_callback) {
+        my $content = $response->content;
+        $response->content("$callback($content)");
+
+    }
+
+    # Use $cgi->header properly instead of just printing text directly.
+    # This fixes various problems, including sending Bugzilla's cookies
+    # properly.
     my $headers = $response->headers;
     my @header_args;
     foreach my $name ($headers->header_field_names) {
@@ -118,6 +126,10 @@ sub retrieve_json_from_get {
     # before _handle.
     $self->{_bz_request_id} = $input{id} = $id;
 
+    # _bz_callback can throw an error, so we have to set it here, after we're
+    # ready to throw errors.
+    $self->_bz_callback(scalar $cgi->param('callback'));
+
     if (!$cgi->param('method')) {
         ThrowUserError('json_rpc_get_method_required');
     }
@@ -345,11 +357,30 @@ sub _argument_type_check {
     return $params;
 }
 
+##########################
+# Private Custom Methods #
+##########################
+
 # _bz_method_name is stored by _find_procedure for later use.
 sub _bz_method_name {
     return $_[0]->{_bz_method_name}; 
 }
 
+sub _bz_callback {
+    my ($self, $value) = @_;
+    if (defined $value) {
+        $value = trim($value);
+        # We don't use \w because we don't want to allow Unicode here.
+        if ($value !~ /^[A-Za-z0-1_\.\[\]]+$/) {
+            ThrowUserError('json_rpc_invalid_callback', { callback => $value });
+        }
+        $self->{_bz_callback} = $value;
+        # JSONP needs to be parsed by a JS parser, not by a JSON parser.
+        $self->content_type('text/javascript');
+    }
+    return $self->{_bz_callback};
+}
+
 1;
 
 __END__
@@ -419,6 +450,36 @@ what version of the JSON-RPC protocol you're using, and C<id> as a URL
 parameter if you want there to be a specific C<id> value in the returned
 JSON-RPC response.
 
+=head2 JSONP
+
+When calling the JSON-RPC WebService over GET, you can use the "JSONP"
+method of doing cross-domain requests, if you want to access the WebService
+directly on a web page from another site. JSONP is described at
+L<http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp/>.
+
+To use JSONP with Bugzilla's JSON-RPC WebService, simply specify a
+C<callback> parameter to jsonrpc.cgi when using it via GET as described above.
+For example, here's some HTML you could use to get the data from 
+C<Bugzilla.time> on a remote website, using JSONP:
+
+ <script type="text/javascript" 
+         src="http://bugzilla.example.com/jsonrpc.cgi?method=Bugzilla.time&amp;callback=foo">
+
+That would call the C<Bugzilla.time> method and pass its value to a function
+called C<foo> as the only argument. All the other URL parameters (such as
+C<params>, for passing in arguments to methods) that can be passed to
+C<jsonrpc.cgi> during GET requests are also available, of course. The above
+is just the simplest possible example.
+
+The values returned when using JSONP are identical to the values returned
+when not using JSONP, so you will also get error messages if there is an
+error.
+
+The C<callback> URL parameter may only contain letters, numbers, periods, and
+the underscore (C<_>) character. Including any other characters will cause
+Bugzilla to throw an error. (This error will be a normal JSON-RPC response,
+not JSONP.)
+
 =head1 PARAMETERS
 
 For JSON-RPC 1.0, the very first parameter should be an object containing
index d866f301d8e7485c0d22e4f36cdb0a7f8e7b7e3b..698eed8fb23c8af67685ad74b966107a95e760a7 100644 (file)
     Error: [% err_msg FILTER html %]
     Value: [% params FILTER html %]
 
+  [% ELSIF error == "json_rpc_invalid_callback" %]
+    You cannot use '[% callback FILTER html %]' as your 'callback' parameter.
+    For security reasons, only letters, numbers, and the following
+    characters are allowed in the 'callback' parameter: <code>[]._</code>
+
   [% ELSIF error == "json_rpc_post_only" %]
     For security reasons, you may only call the '[% method FILTER html %]'
     method via HTTP POST.