From 62efd548bbd87e4018cee195e6c74cd8a4e13d7b Mon Sep 17 00:00:00 2001 From: Max Kanat-Alexander Date: Tue, 27 Apr 2010 12:14:36 -0700 Subject: Bug 550727: Add JSONP Support to the JSON-RPC WebService Interface r=dkl, a=mkanat --- Bugzilla/WebService/Server/JSONRPC.pm | 69 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 4 deletions(-) (limited to 'Bugzilla') diff --git a/Bugzilla/WebService/Server/JSONRPC.pm b/Bugzilla/WebService/Server/JSONRPC.pm index ae479ff5e..5ab5e4a7b 100644 --- a/Bugzilla/WebService/Server/JSONRPC.pm +++ b/Bugzilla/WebService/Server/JSONRPC.pm @@ -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 as a URL parameter if you want there to be a specific C 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. + +To use JSONP with Bugzilla's JSON-RPC WebService, simply specify a +C 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 on a remote website, using JSONP: + +