diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Qooxdoo/JSONRPC.pm | 188 |
1 files changed, 148 insertions, 40 deletions
diff --git a/lib/Qooxdoo/JSONRPC.pm b/lib/Qooxdoo/JSONRPC.pm index b948ffb..cf6d11b 100644 --- a/lib/Qooxdoo/JSONRPC.pm +++ b/lib/Qooxdoo/JSONRPC.pm @@ -28,6 +28,10 @@ use JSON; # Enabling debugging will log information in the apache logs, and in # some cases provide more information in error responses $Qooxdoo::JSONRPC::debug = 0; +# By default methods are located in the Qooxdoo/Services directory +$Qooxdoo::JSONRPC::service_path = 'Qooxdoo::Services'; +# By default methods have to be prefixed by 'method_' +$Qooxdoo::JSONRPC::method_prefix = 'method_'; ############################################################################## @@ -72,10 +76,10 @@ sub handle_request my $session_id = $session->id (); - print STDERR "Session id: $session_id\n" + print STDERR "Session id: $session_id\n" if $Qooxdoo::JSONRPC::debug; - print $session->header; + print $session->header(-charset=>'UTF-8'); # 'selfconvert' is enabled for date conversion. Ideally we also want # 'convblessed', but this then disabled 'selfconvert'. @@ -127,7 +131,6 @@ sub handle_request $error->set_script_transport_id ($script_transport_id); $input = $cgi->param ('_ScriptTransport_data'); - } else { @@ -209,7 +212,7 @@ sub handle_request # Generate the name of the module corresponding to the Service - my $module = join ('::', ('Qooxdoo', 'Services', @service_components)); + my $module = join ('::', ($Qooxdoo::JSONRPC::service_path, @service_components)); # Attempt to load the module @@ -235,7 +238,6 @@ sub handle_request "Service '$module' not found"); } $error->send_and_exit; - } #---------------------------------------------------------------------- @@ -247,7 +249,7 @@ sub handle_request my $accessibility = defaultAccessibility; my $accessibility_method = "${module}::GetAccessibility"; - + if (defined $accessibility_method) { print STDERR "Module $module has GetAccessibility\n" @@ -260,7 +262,7 @@ sub handle_request if ($@) { print STDERR "$@\n" if $Qooxdoo::JSONRPC::debug; - + $error->set_error (JsonRpcError_Unknown, $@); $error->send_and_exit; @@ -310,12 +312,12 @@ sub handle_request "Permission denied"); $error->send_and_exit; } - + if (!defined $session->param ('session_referer_domain')) { $session->param ('session_referer_domain', $refererDomain); } - + } elsif ($accessibility eq Accessibility_Session) { @@ -358,8 +360,8 @@ sub handle_request # Generate the name of the function to call and check it exists - my $package_method = "${module}::method_${method}"; - + my $package_method = ${module}.'::'.$Qooxdoo::JSONRPC::method_prefix.$method; + unless (defined &$package_method) { $error->set_error (JsonRpcError_MethodNotFound, @@ -410,7 +412,6 @@ sub handle_request $error->set_error (JsonRpcError_Unknown, $@); $error->send_and_exit; - } # (I've had to assume this behaviour based on the test results) @@ -812,7 +813,7 @@ sub blessed_date $millisecond); } - +__END__ ############################################################################## @@ -822,10 +823,102 @@ Qooxdoo::JSONRPC.pm - A Perl implementation of JSON-RPC for Qooxdoo =head1 SYNOPSIS +The cgi/fcgi script + + #!/usr/bin/perl -w + use strict; + use CGI; + use CGI::Session::Driver::file; + # make sure session files do not clash + $CGI::Session::Driver::file::FileName = + ($ENV{USER}||'').$0.'%s.session'; + + use Qooxdoo::JSONRPC; + + # talk about what we do in the apache error log + #$Qooxdoo::JSONRPC::debug = 0; + + # By default methods are located in Qooxdoo/Services + #$Qooxdoo::JSONRPC::service_path = 'Qooxdoo::Services'; + + # By default methods have to be prefixed by 'method_' + #$Qooxdoo::JSONRPC::method_prefix = 'method_'; + + my $cgi = new CGI; + my $session = new CGI::Session; + Qooxdoo::JSONRPC::handle_request ($cgi, $session); + + # or if you load CGI::Fast you can easily create a + # fastcgi aware version + #while (my $cgi = new CGI::Fast) { + # my $session = new CGI::Session; + # Qooxdoo::JSONRPC::handle_request ($cgi, $session); + #} + +Along with the cgi wrapper setup a service module the example +below shows how to handle logins on the server side. + + package Qooxdoo::Services::Demo; + use strict; + + sub GetAccessibility { + # name of method about to be called + my $method = shift; + # access level based on connection + my $access = shift; + # session handle + my $session = shift; + if ($method eq 'login' + or $method eq 'logoff' + or $session->param('authenticated')||'' eq 'yes'){ + return 'public'; # grant everyone access + } + else { + return 'fail'; #deny access + } + } + + sub method_login { + my $error = shift; + my $session = $error->{session}; + my @args = @_; + # if login ok + $session->param('authenticated','yes'); + $session->flush(); + return 1; + # if login is not ok + $error->set_error(101,'Login faild'); + return $error; + } + + sub method_logout { + my $error = shift; + my $session = $error->{session}; + $session->delete(); + $session->flush(); + return 1; + } + + sub method_fun { + my $error = shift; + my $session = $error->{session}; + my $arg_a = shift; + my $arg_b = shift; + # do something + return $pointer_to_data; + } + + 1; + +=head1 DESCRIPTION + RPC-JSON is a straightforward Remote Procedure Call mechanism, primarily -targeted at Javascript clients, and hence ideal for Qooxdoo. +targeted at Javascript clients, and hence ideal for Qooxdoo. This module +implements the server side handling of RPC request in perl. -Services may be implemented in any language provided they provide a +=head2 JSON RPC Basics + +JSON-RPC Services may be implemented in any language provided they provide a conformant implementation. This module uses the CGI module to parse HTTP headers, and the JSON module to manipulate the JSON body. @@ -840,7 +933,9 @@ server->client: {"id":1,"result":"Client said: [Hello]"} Here the service 'qooxdoo.test' is requested to run a method called -'echo' with an argument 'Hello'. This Perl implementation will locate +'echo' with an argument 'Hello'. + +This Perl implementation will locate a module called Qooxdoo::Services::qooxdoo::test (corresponding to Qooxoo/Services/qooxdoo/test.pm in Perl's library path). It will then execute the function Qooxdoo::Services::qooxdoo::test::echo @@ -865,21 +960,21 @@ response is formatted something like: There are 4 error origins: -=over 4 +=over -=item * JsonRpcError_Origin_Server 1 +=item JsonRpcError_Origin_Server 1 The error occurred within the server. -=item * JsonRpcError_Origin_Application 2 +=item JsonRpcError_Origin_Application 2 The error occurred within the application. -=item * JsonRpcError_Origin_Transport 3 +=item JsonRpcError_Origin_Transport 3 The error occurred somewhere in the communication (not raised in this module). -=item * JsonRpcError_Origin_Client 4 +=item JsonRpcError_Origin_Client 4 The error occurred in the client (not raised in this module). @@ -887,79 +982,92 @@ The error occurred in the client (not raised in this module). For Server errors, there are also some predefined error codes. -=over 4 +=over -=item * JsonRpcError_Unknown 0 +=item JsonRpcError_Unknown 0 The cause of the error was not known. -=item * JsonRpcError_IllegalService 1 +=item JsonRpcError_IllegalService 1 The Service name was not valid, typically due to a bad character in the name. -=item * JsonRpcError_ServiceNotFound 2 +=item JsonRpcError_ServiceNotFound 2 The Service was not found. In this implementation this means that the module containing the Service could not be found in the library path. -=item * JsonRpcError_ClassNotFound 3 +=item JsonRpcError_ClassNotFound 3 This means the class could not be found with, is not actually raised by this implementation. -=item * JsonRpcError_MethodNotFound 4 +=item JsonRpcError_MethodNotFound 4 The method could not be found. This is raised if a function cannot be found with the method name in the requested package namespace. Note: In Perl, modules (files containing functionality) and packages (namespaces) are closely knitted together, but there need not be a -one-for-one correspondence -- packages can be shared across multiple +one-to-one correspondence -- packages can be shared across multiple modules, or a module can use multiple packages. This module assumes a -one-for-one correspondence by looking for the method in the same +one-to-one correspondence by looking for the method in the same namespace as the module name. -=item * JsonRpcError_ParameterMismatch 5 +=item JsonRpcError_ParameterMismatch 5 This is typically raised by individual methods when they do not receive the parameters they are expecting. -=item * JsonRpcError_PermissionDenied 6 +=item JsonRpcError_PermissionDenied 6 Again, this error is raised by individual methods. Remember that RPC calls need to be as secure as the rest of your application! =back -There is also some infrastructure to allow access control on methods -depending on the relationship of the referer. Have a look at test.pm -to see how this can be done by defining C<GetAccessibility> which -returns one of the following for a supplied method name: +=head2 Access Control + +There is also some infrastructure to implement access control. Before +each method call, the C<GetAccessibility> method of the service is +called. Depending on the response from C<GetAccessibility> the actual +method will be called, or an error is returned to the remote caller. +The example in the synopys shows how to use that for implementing an +actuall authentication process. -=over 4 +C<GetAccessibility> must return the following access levels -=item * Accessibility_Public ("public") +=over + +=item Accessibility_Public ("public") The method may be called from any session, and without any checking of who the Referer is. -=item * Accessibility_Domain ("domain") +=item Accessibility_Domain ("domain") The method may only be called by a script obtained via a web page loaded from this server. The Referer must match the request URI, through the domain part. -=item * Accessibility_Session ("session") +=item Accessibility_Session ("session") The Referer must match the Referer of the very first RPC request issued during the session. -=item * Accessibility_Fail ("fail") +=item Accessibility_Fail ("fail") Access is denied =back +=head2 Persistant Data in the Session module + +Methods get access to the session as a parameter of the error object. +Session allows to easily store persistant data in methods. Since +the session module writes all parameters in one go, this can +result in a race condition when two instances store data. + =head1 AUTHOR Nick Glencross E<lt>nick.glencross@gmail.comE<gt> |