File Coverage

lib/Catalyst/Plugin/Server/JSONRPC.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Catalyst::Plugin::Server::JSONRPC -- Catalyst JSONRPC Server Plugin
5              
6             =head1 SYNOPSIS
7              
8             package MyApp;
9             use Catalyst qw/Server Server::JSONRPC/;
10              
11             package MyApp::Controller::Example;
12             use base 'Catalyst::Controller';
13              
14             sub echo : JSONRPC { # available as: example.echo
15             my ( $self, $c, @args ) = @_;
16             $c->stash->{jsonrpc} = join ', ', @args;
17             }
18              
19             sub ping : JSONRPCPath('/ping') { # available as: ping
20             my ( $self, $c ) = @_;
21             $c->stash->{jsonrpc} = 'Pong';
22             }
23              
24             sub world : JSONRPCRegex(/hello/) { # available as: *hello*
25             my ($self, $c) = @_;
26             $c->stash->{jsonrpc} = 'World';
27             }
28              
29             sub echo : JSONRPCLocal { # available as: example.echo
30             my ( $self, $c, @args ) = @_;
31             $c->stash->{jsonrpc} = join ', ', @args;
32             }
33              
34             sub ping : JSONRPCGlobal { # available as: ping
35             my ( $self, $c ) = @_;
36             $c->stash->{jsonrpc} = 'Pong';
37             }
38              
39             =head1 DESCRIPTION
40              
41             JSONRPC Plugin for Catalyst which we tried to make compatible with the
42             way Catalyst works with URLS. Main features are:
43              
44             =over 4
45              
46             =item * Split JSONRPC methodNames by STRING to find out Controller.
47              
48             =item * Single entrypoint for JSONRPC calls, like http://host.tld/rpc
49              
50             =item * DispatchTypes (attributes) which work much the same as Catalyst attrs
51              
52             =item * JSONRPC Parameter handling transparent to Catalyst parameter handling
53              
54             =back
55              
56             =head1 HOW IT WORKS
57              
58             The default behaviour will handle JSONRPC Requests sent to C</rpc> by creating
59             an OBJECT containing JSONRPC specific parameters in C<< $c->req->jsonrpc >>.
60              
61             Directly after, it will find out the Path of the Action to dispatch to, by
62             splitting methodName by C<.>:
63              
64             methodName: hello.world
65             path : /hello/world
66              
67             From this point, it will dispatch to '/hello/world' when it exists,
68             like Catalyst Urls would do. What means: you will be able to set Regexes,
69             Paths etc on subroutines to define the endpoint.
70              
71             We discuss these custom JSONRPC attributes below.
72              
73             When the request is dispatched, we will return $c->stash->{jsonrpc} to the
74             jsonrpc client, or, when it is not available, it will return $c->stash to
75             the client. There is also a way of defining $c->stash keys to be send back
76             to the client.
77              
78             =head1 ATTRIBUTES
79              
80             You can mark any method in your Catalyst application as being
81             available remotely by using one of the following attributes,
82             which can be added to any existing attributes, except Private.
83             Remember that one of the mentioned attributes below are automatically
84             also Privates...
85              
86             =over 4
87              
88             =item JSONRPC
89              
90             Make this method accessible via JSONRPC, the same way as Local does
91             when using catalyst by URL.
92              
93             The following example will be accessible by method C<< hello.world >>:
94              
95             package Catalyst::Controller::Hello
96             sub world : JSONRPC {}
97              
98             =item JSONRPCLocal
99              
100             Identical version of attribute C<JSONRPC>
101              
102             =item JSONRPCGlobal
103              
104             Make this method accessible via JSONRPC, the same way as GLOBAL does
105             when using catalyst by URL.
106              
107             The following example will be accessible by method C<< ping >>:
108              
109             package Catalyst::Controller::Hello
110             sub ping : JSONRPCGlobal {}
111              
112             =item JSONRPCPath('/say/hello')
113              
114             Make this method accessible via JSONRPC, the same way as Path does
115             when using catalyst by URL.
116              
117             The following example will be accessible by method C<< say.hello >>:
118              
119             package Catalyst::Controller::Hello
120             sub hello : JSONRPCPath('/say/hello') {}
121              
122             =item JSONRPCRegex('foo')
123              
124             Make this method accessible via JSONRPC, the same way as Regex does
125             when using catalyst by URL.
126              
127             The following example will be accessible by example methods:
128             C<< a.foo.method >>
129             C<< wedoofoohere >>
130             C<< foo.getaround >>
131              
132             package Catalyst::Controller::Hello
133             sub hello : JSONRPCPath('foo') {}
134              
135             =back
136              
137             =head1 ACCESSORS
138              
139             Once you've used the plugin, you'll have an $c->request->jsonrpc accessor
140             which will return an C<Catalyst::Plugin::Server::JSONRPC> object.
141              
142             You can query this object as follows:
143              
144             =over 4
145              
146             =item $c->req->jsonrpc->is_jsonrpc_request
147              
148             Boolean indicating whether the current request has been initiated
149             via JSONRPC
150              
151             =item $c->req->jsonrpc->config
152              
153             Returns a C<Catalyst::Plugin::Server::JSONRPC::Config> object. See the
154             C<CONFIGURATION> below on how to use and configure it.
155              
156             =item $c->req->jsonrpc->body
157              
158             The body of the original JSONRPC call
159              
160             =item $c->req->jsonrpc->method
161              
162             The name of the original method called via JSONRPC
163              
164             =item $c->req->jsonrpc->args
165              
166             A list of parameters supplied by the JSONRPC call
167              
168             =item $c->req->jsonrpc->result_as_string
169              
170             The JSON body that will be sent back to the JSONRPC client
171              
172             =item $c->req->jsonrpc->error
173              
174             Allows you to set jsonrpc fault code and message
175              
176             =back
177              
178             =head1 Server Accessors
179              
180             The following accessors are always available, whether you're in a jsonrpc
181             specific request or not
182              
183             =over 4
184              
185             =item $c->server->jsonrpc->list_methods
186              
187             Returns a HASHREF containing the available jsonrpc methods in Catalyst as
188             a key, and the C<Catalyst::Action> object as a value.
189              
190             =back
191              
192             =head1 CATALYST REQUEST
193              
194             To make things transparent, we try to put JSONRPC params into the Request
195             object of Catalyst. But first we will explain something about the JSONRPC
196             specifications.
197              
198             A full draft of these specifications can be found on:
199             C<http://www.jsonrpc.com/spec>
200              
201             In short, a jsonrpc-request consists of a methodName, like a subroutine
202             name, and a list of parameters. This list of parameters may contain strings
203             (STRING), arrays (LIST) and structs (HASH). Off course, these can be nested.
204              
205             =over 4
206              
207             =item $c->req->arguments
208              
209             We will put the list of arguments into $c->req->arguments, thisway you can
210             fetch this list within your dispatched-to-subroutine:
211              
212             sub echo : JSONRPC {
213             my ($self, $c, @args) = @_;
214             $c->log->debug($arg[0]); # Prints first JSONRPC parameter
215             # to debug log
216             }
217              
218             =item $c->req->parameters
219              
220             Because JSONRPC parameters are a LIST, we can't B<just> fill
221             $c->req->paremeters. To keep things transparent, we made an extra config
222             option what tells the JSONRPC server we can assume the following conditions
223             on all JSONRPC requests:
224             - There is only one JSONRPC parameter
225             - This JSONRPC parameter is a struct (HASH)
226              
227             We will put this STRUCT as key-value pairs into $c->req->parameters.
228              
229             =item $c->req->params
230              
231             Alias of $c->req->parameters
232              
233             =item $c->req->param
234              
235             Alias of $c->req->parameters
236              
237             =back
238              
239             =cut
240              
241             {
242              
243             package Catalyst::Plugin::Server::JSONRPC;
244              
245             our $VERSION = "0.07";
246              
247 1     1   24147 use strict;
  1         2  
  1         23  
248 1     1   5 use warnings;
  1         2  
  1         29  
249 1     1   1937 use attributes ();
  1         8379  
  1         45  
250              
251 1     1   13981 use Data::Dumper;
  1         19345  
  1         93  
252 1     1   484 use JSON::RPC::Common::Procedure::Return;
  0            
  0            
253             use JSON::RPC::Common::Marshal::HTTP;
254             use MRO::Compat;
255              
256             my $ServerClass = 'Catalyst::Plugin::Server::JSONRPC::Backend';
257              
258             ### only for development dumps!
259             my $Debug = 0;
260              
261             ###
262             ### Catalyst loading and dispatching
263             ###
264              
265             ### Loads our jsonrpc backend class in $c->server->jsonrpc
266             sub setup_engine {
267             my $class = shift;
268             $class->server->register_server( 'jsonrpc' => $ServerClass->new($class) );
269             $class->next::method(@_);
270             }
271            
272             sub setup {
273             my $class = shift;
274             ### config is not yet loaded on setup_engine so load it here
275             $class->server->jsonrpc->config( Catalyst::Plugin::Server::JSONRPC::Config->new($class) );
276             $class->next::method(@_);
277             }
278            
279             ### Will load our customized DispatchTypes into Catalyst
280             sub setup_dispatcher {
281             my $class = shift;
282             ### Load custom DispatchTypes
283             $class->next::method(@_);
284             $class->dispatcher->preload_dispatch_types(
285             @{ $class->dispatcher->preload_dispatch_types },
286             qw/ +Catalyst::Plugin::Server::JSONRPC::DispatchType::JSONRPCPath
287             +Catalyst::Plugin::Server::JSONRPC::DispatchType::JSONRPCRegex/
288             );
289              
290             return $class;
291             }
292              
293             ### Loads the jsonrpc-server object, redispatch to the method
294             sub prepare_action {
295             my $c = shift;
296             my @args = @_;
297              
298             ### set up the accessor to hold an jsonrpc server instance
299             $c->req->register_server( 'jsonrpc' => Catalyst::Plugin::Server::JSONRPC::Request->new() );
300              
301             ### are we an jsonrpc call? check the path against a regex
302             my $path = $c->server->jsonrpc->config->path;
303             if ( $c->req->path =~ /$path/ ) {
304              
305             PREPARE: {
306             ### mark us as an jsonrpc request
307             $c->req->jsonrpc->is_jsonrpc_request(1);
308              
309             $c->stash->{current_view_instance} = $c->server->jsonrpc->view_instance;
310              
311             $c->log->debug( 'PREPARE WITH $c ' . Dumper($c) ) if $Debug;
312              
313             $c->req->jsonrpc->_deserialize_json($c) or last PREPARE;
314              
315             ### CAVEAT: we consider backing up to a default for a
316             ### json-rpc method when the method doesn't exist a security
317             ### risk. So when the exact method doesn't exist, we return
318             ### an error.
319             ### TODO ARGH Because of regex methods, this won't work
320              
321             ### set the new request path, the one we will forward to
322             $c->req->path( $c->req->jsonrpc->forward_path );
323              
324             ### filter change dispatch types to our OWN
325             {
326             my $saved_dt = $c->dispatcher->dispatch_types || [];
327             my $dp_ns = 'Catalyst::Plugin::Server::JSONRPC::DispatchType::';
328              
329             $c->dispatcher->dispatch_types(
330             [
331             grep {
332             UNIVERSAL::isa( $_, $dp_ns . 'JSONRPCPath' )
333             or UNIVERSAL::isa( $_, $dp_ns . 'JSONRPCRegex' )
334             } @$saved_dt
335             ]
336             );
337              
338             ### run the rest of the prepare actions, we should have
339             ### an action object now
340             $c->next::method(@_);
341              
342             ### restore the saved dispatchtypes
343             $c->dispatcher->dispatch_types($saved_dt);
344             }
345              
346             ### check if we have a c->action now
347             ### check if the NEW action isn't hte same as the
348             ### OLD action -- which mean no method was found
349             ### Not needed, don't have an action until we NEXT
350             if ( ( not $c->action )
351             && !$c->server->jsonrpc->private_methods->{ $c->req->jsonrpc->method } )
352             {
353             $c->req->jsonrpc->_error( $c, qq[Invalid JSONRPC request: No such method] );
354             last PREPARE;
355             }
356             }
357              
358             ### JSONRPC parameters and argument processing, see the Request
359             ### class below for information why we can't do it there.
360             $c->req->parameters( $c->req->jsonrpc->params )
361             if $c->server->jsonrpc->config->convert_params;
362              
363             $c->req->args( $c->req->jsonrpc->args );
364              
365             ### we're no jsonrpc request, so just let others handle it
366             } else {
367             $c->next::method(@_);
368             }
369             }
370              
371             ### before we dispatch, make sure no jsonrpc errors have happened already,
372             ### or an internal method has been called.
373             sub dispatch {
374             my $c = shift;
375             if ( $c->req->jsonrpc->is_jsonrpc_request
376             and scalar( @{ $c->error } ) )
377             {
378             1;
379             } elsif ( $c->req->jsonrpc->is_jsonrpc_request
380             and $c->server->jsonrpc->private_methods->{ $c->req->jsonrpc->method } )
381             {
382             $c->req->jsonrpc->run_method($c);
383             } else {
384             $c->next::method(@_);
385             }
386             }
387              
388             sub finalize {
389             my $c = shift;
390              
391             if ( $c->req->jsonrpc->is_jsonrpc_request ) {
392              
393             #XXX if they skipped Catalust::View::JSONRPC - run it
394              
395             $c->stash->{current_view_instance}->process($c) unless $c->stash->{jsonrpc_generated};
396              
397             #drop all errors;
398             $c->error(0);
399             }
400             $c->log->debug( 'FINALIZE ' . Dumper( $c, \@_ ) ) if $Debug;
401              
402             ### always call finalize at the end, so Catalyst's final handler
403             ### gets called as well
404             $c->next::method(@_);
405              
406             }
407              
408             }
409              
410             ### The server implementation
411             {
412              
413             package Catalyst::Plugin::Server::JSONRPC::Backend;
414              
415             use base qw/Class::Accessor::Fast/;
416             use Data::Dumper;
417              
418             __PACKAGE__->mk_accessors(
419             qw/
420             dispatcher
421             private_methods
422             c
423             config
424             view_instance
425             /
426             );
427              
428             sub new {
429             my $class = shift;
430             my $c = shift;
431             my $self = $class->SUPER::new(@_);
432              
433             $self->c($c);
434             ### config is not yet loaded on setup_engine
435             #$self->config( Catalyst::Plugin::Server::JSONRPC::Config->new($c) );
436             $self->private_methods( {} );
437             $self->dispatcher( {} );
438             $self->view_instance( Catalyst::View::JSONRPC->new );
439              
440             ### Internal function
441             $self->add_private_method(
442             'system.listMethods' => sub {
443             my ( $c_ob, @args ) = @_;
444             return [
445             keys %{
446             $c_ob->server->jsonrpc->list_methods;
447             }
448             ];
449             }
450             );
451              
452             return $self;
453             }
454              
455             sub add_private_method {
456             my ( $self, $name, $sub ) = @_;
457              
458             return unless ( $name && UNIVERSAL::isa( $sub, 'CODE' ) );
459             $self->private_methods->{$name} = $sub;
460             return 1;
461             }
462              
463             sub list_methods {
464             my ($self) = @_;
465             return $self->dispatcher->{Path}->methods( $self->c );
466             }
467             }
468              
469             ### the config implementation ###
470             {
471              
472             package Catalyst::Plugin::Server::JSONRPC::Config;
473             use base 'Class::Accessor::Fast';
474              
475             ### XXX change me to an ENTRYPOINT!
476             my $DefaultPath = qr!^(/?)rpc(/|$)!i;
477             my $DefaultAttr = 'JSONRPC';
478             my $DefaultPrefix = '';
479             my $DefaultSep = '.';
480             my $DefaultShowErrors = 0;
481              
482             ### XXX add: stash_fields (to encode) stash_exclude_fields (grep -v)
483              
484             __PACKAGE__->mk_accessors(
485             qw/ path prefix separator attribute convert_params
486             show_errors
487             /
488             );
489              
490             ### return the cached version where possible
491             my $Obj;
492              
493             sub new {
494             return $Obj if $Obj;
495              
496             my $class = shift;
497             my $c = shift;
498             my $self = $class->SUPER::new;
499              
500             $self->prefix( $c->config->{jsonrpc}->{prefix} || $DefaultPrefix );
501             $self->separator( $c->config->{jsonrpc}->{separator} || $DefaultSep );
502             $self->path( $c->config->{jsonrpc}->{path} || $DefaultPath );
503             $self->show_errors( $c->config->{jsonrpc}->{show_errors} || $DefaultShowErrors );
504             $self->attribute($DefaultAttr);
505             $self->convert_params(1);
506              
507             ### cache it
508             return $Obj = $self;
509             }
510             }
511              
512             ### the server class implementation ###
513             {
514              
515             package Catalyst::Plugin::Server::JSONRPC::Request;
516              
517             use strict;
518             use warnings;
519              
520             use JSON::RPC::Common::Marshal::Catalyst;
521             use JSON::RPC::Common::Procedure::Call;
522             use JSON::RPC::Common::Procedure::Return;
523              
524             use Data::Dumper;
525             use Text::SimpleTable;
526              
527             use base 'Class::Data::Inheritable';
528             use base 'Class::Accessor::Fast';
529              
530             __PACKAGE__->mk_accessors(
531             qw[ forward_path method result args body
532             is_jsonrpc_request params
533             result_as_string internal_methods error
534             ]
535             );
536              
537             __PACKAGE__->mk_classdata(qw[_jsonrpc_parser]);
538             __PACKAGE__->mk_classdata(qw[call]);
539             __PACKAGE__->_jsonrpc_parser( JSON::RPC::Common::Marshal::Catalyst->new );
540              
541             *parameters = *params;
542              
543             sub run_method {
544             my ( $self, $c, @args ) = @_;
545              
546             $c->stash->{jsonrpc} =
547             &{ $c->server->jsonrpc->private_methods->{ $self->method } }( $c, $self->call->params_list, @args );
548              
549             }
550              
551             sub _deserialize_json {
552             my ( $self, $c ) = @_;
553              
554             ### the parser will die on failure, make sure we catch it
555             my $call;
556             eval { $call = $self->_jsonrpc_parser->request_to_call( $c->req ); };
557             ### parsing the request went fine
558             if ( not $@ and defined $call->method ) {
559              
560             $self->call($call); # original json call
561             $self->method( $call->method ); # name of the method
562             $self->body( $self->_jsonrpc_parser->call_to_json($call) ); #
563              
564             ### allow the args to be encoded as a HASH when requested
565             ### jsonrpc only knows a top level 'list', and we can not tell
566             ### if that is meant to be a hash or not
567             ### make sure to store args as an ARRAY REF! to be compatible
568             ### with catalyst
569              
570             my $p = $call->params;
571              
572             if ( ref $p eq 'HASH' ) {
573             $self->params($p);
574             $self->args(%$p); # parsed arguments
575             } elsif ( ref $p eq 'ARRAY' ) {
576             $self->args($p); # parsed arguments
577             $self->params( {} );
578             } else {
579             $self->args( [] );
580             $self->params( {} );
581             }
582              
583             ### build the relevant namespace, action and path
584             { ### construct the forward path -- this allows catalyst to
585             ### do the hard work of dispatching for us
586             my $prefix = $c->server->jsonrpc->config->prefix;
587             my ($sep) = map { qr/$_/ }
588             map { quotemeta $_ } $c->server->jsonrpc->config->separator;
589              
590             ### error checks here
591             if ( $prefix =~ m|^/| ) {
592             $c->log->debug( __PACKAGE__ . ": Your prefix starts with" . " a / -- This is not recommended" )
593             if $c->debug;
594             }
595              
596             unless ( UNIVERSAL::isa( $sep, 'Regexp' ) ) {
597             $c->log->debug( __PACKAGE__ . ": Your separator is not a " . "Regexp object -- This is not recommended" )
598             if $c->debug;
599             }
600              
601             ### foo.bar => $prefix/foo/bar
602             ### DO NOT add a leading slash! uri.pm gets very upset
603             my @parts = split( $sep, $self->method );
604             my $fwd_path = join '/', grep { defined && length } $prefix, @parts;
605              
606             ### Complete our object-instance
607             $self->forward_path($fwd_path);
608              
609             ### Notify system of called rpc method and arguments
610             $c->log->debug( 'JSON-RPC: Method called: ' . $self->method )
611             if $c->debug;
612             if ( $c->server->jsonrpc->config->convert_params
613             && $self->params )
614             {
615             my $params = Text::SimpleTable->new( [ 36, 'Key' ], [ 37, 'Value' ] );
616             foreach my $key ( sort keys %{ $self->params } ) {
617             my $value = $self->params->{$key};
618             $value = ref($value) || $value;
619             $params->row( $key, $value );
620             }
621             $c->log->debug( "JSON-RPC: Parameters:\n" . $params->draw )
622             if ( $c->debug && %{ $self->params } );
623             }
624             }
625              
626             ### an error in parsing the request
627             } elsif ($@) {
628             $self->_error( $c, qq[Invalid JSONRPC request "$@"] );
629             return;
630              
631             ### something is wrong, but who knows what...
632             } else {
633             $self->_error( $c, qq[Invalid JSONRPC request: Unknown error] );
634             return;
635             }
636              
637             return $self;
638             }
639              
640             ### alias arguments to args
641             *arguments = *args;
642              
643             ### record errors in the error and debug log -- just for convenience
644             sub _error {
645             my ( $self, $c, $msg ) = @_;
646             $c->log->debug($msg) if $c->debug;
647             $c->error($msg);
648             }
649             }
650              
651             {
652              
653             package Catalyst::View::JSONRPC;
654              
655             use strict;
656             use warnings;
657              
658             use base 'Catalyst::View';
659              
660             sub process {
661             my $self = shift;
662             my $c = $_[0];
663             ### if we got an error anywhere, we'll return a fault
664             ### othwerise, the resultset will be returned
665             ### XXX $c->error
666             my $res;
667             my $req_error = $c->req->jsonrpc->error;
668             my $error;
669             my $ecode = 200;
670             if ( $req_error || scalar( @{ $c->error } ) ) {
671             if ( $c->server->jsonrpc->config->show_errors ) {
672             if ( $req_error && ref $req_error eq 'ARRAY' ) {
673             ( $ecode, $error ) = @{$req_error};
674             } else {
675             $error = join $/, @{ $c->error };
676             }
677             } else {
678             $c->log->debug( "JSONRPC 500 Errors:\n" . join( "\n", @{ $c->error } ) );
679             $error = 'Internal Server Error';
680             $ecode = 500;
681             }
682             } else {
683             if ( exists $c->stash->{jsonrpc} ) {
684             $res = $c->stash->{jsonrpc};
685             } elsif ( $c->res->body ) {
686             $res = $c->res->body;
687             } else {
688             $res = $c->stash;
689             delete $res->{current_view_instance};
690             }
691             $c->res->body(undef);
692              
693             }
694             use Data::Dumper;
695              
696             my $result;
697             if ($error) {
698             if ( $c->req->jsonrpc->call ) {
699             ### XXX play with return error due to possible return_error bug
700             ### in JSON::RPC::Common:Return::Error
701             my $class = $c->req->jsonrpc->call->error_class;
702             my $err = $class->new(
703             version => $c->req->jsonrpc->call->version,
704             message => $error,
705             code => $ecode,
706             );
707             $result = $c->req->jsonrpc->call->return_error($err);
708             } else {
709             die "ERROR: " . $error;
710             }
711             } else {
712             $result = $c->req->jsonrpc->call->return_result($res);
713             }
714              
715             #my $writer = JSON::RPC::Common::Marshal::HTTP->new();
716             $c->req->jsonrpc->_jsonrpc_parser->write_result_to_response( $result, $c->res );
717             $c->res->status(200) if $c->server->jsonrpc->config->show_errors;
718             ### make sure to clear the error, so catalyst doesn't try
719             ### to deal with it
720             $c->stash->{jsonrpc_generated} = 1;
721             $c->error(0);
722             }
723             }
724             1;
725              
726             __END__
727              
728             =head1 INTERNAL JSONRPC FUNCTIONS
729              
730             The following system functions are available to the public.,
731              
732             =over 4
733              
734             =item system.listMethods
735              
736             returns a list of available RPC methods.
737              
738             =back
739              
740             =head1 DEFINING RETURN VALUES
741              
742             The JSON-RPC response must contain a single parameter, which may contain
743             an array (LIST), struct (HASH) or a string (STRING). To define the return
744             values in your subroutine, you can alter $c->stash in three different ways.
745              
746             =head2 Defining $c->stash->{jsonrpc}
747              
748             When defining $c->stash->{jsonrpc}, the JSONRPC server will return these values
749             to the client.
750              
751             =head2 When there is no $c->stash->{jsonrpc}
752              
753             When there is no C<< $c->stash->{jsonrpc} >> set, it will return the complete
754             C<< $c->stash >>
755              
756             =head1 CONFIGURATION
757              
758             The JSONRPC Plugin accepts the following configuration options, which can
759             be set in the standard Catalyst way (See C<perldoc Catalyst> for details):
760              
761             Your::App->config( jsonrpc => { key => value } );
762              
763             You can look up any of the config parameters this package uses at runtime
764             by calling:
765              
766             $c->server->jsonrpc->config->KEY
767              
768             =over 4
769              
770             =item path
771              
772             This specifies the entry point for your jsonrpc server; all requests are
773             dispatched from there. This is the url any JSONRCP client should post to.
774             You can change this to any C<Regex> wish.
775              
776             The default is: C<qr!^(/?)rpc(/|$)!i>, which matches on a top-level path
777             begining with C<rpc> preceeded or followed by an optional C</>, like this:
778              
779             http://your-host.tld/rpc
780              
781             =item prefix
782              
783             This specifies the prefix of the forward url.
784              
785             For example, with a prefix of C<rpc>, and a method C<foo>, the forward
786             path would be come C</rpc/foo>.
787              
788             The default is '' (empty).
789              
790             =item separator
791              
792             This is a STRING used to split your method on, allowing you to use
793             a hierarchy in your method calls.
794              
795             For example, with a separator of C<.> the method call C<demo.echo>
796             would be forwarded to C</demo/echo>. To make C<demo_echo> forward to the
797             same path, you would change the separator to C<_>,
798              
799             The default is C<.>, splitting methods on a single C<.>
800              
801             =item convert_params
802              
803             Make the arguments in C<< $c->req->jsonrpc->params >> available as
804             C<< $c->req->params >>.
805              
806             Defaults to true.
807              
808             =item show_errors
809              
810             Make system errors in C<< $c->error >> public to the rpc-caller in a JSON-RPC
811             faultString. When show_errors is false, and your catalyst app generates a
812             fault, it will return an JSON-RPC fault containing error number 500 and error
813             string: "Internal Server Error".
814              
815             Defaults to false.
816              
817             =back
818              
819             =head1 DIAGNOSTICS
820              
821             =over 4
822              
823             =item Invalid JSONRPC request: No such method
824              
825             There is no corresponding method in your application that can be
826             forwarded to.
827              
828             =item Invalid JSONRPC request %s
829              
830             There was an error parsing the JSONRPC request
831              
832             =item Invalid JSONRPC request: Unknown error
833              
834             An unexpected error occurred
835              
836             =back
837              
838             =head1 TODO
839              
840             =over 4
841              
842             =item Make error messages configurable/filterable
843              
844             Right now, whatever ends up on $c->error gets returned to the client.
845             It would be nice to have a way to filter/massage these messages before
846             they are sent back to the client.
847              
848             =item Make stash filterable before returning
849              
850             Just like the error messages, it would be nice to be able to filter the
851             stash before returning so you can filter out keys you don't want to
852             return to the client, or just return a certain list of keys.
853             This all to make transparent use of JSONRPC and web easier.
854              
855             =back
856              
857             =head1 SEE ALSO
858              
859             L<Catalyst::Manual>,
860             L<Catalyst::Request>, L<Catalyst::Response>, L<JSON::RPC::Common>,
861              
862             =head1 ACKNOWLEDGEMENTS
863              
864             For the original implementation of this module:
865              
866             Marcus Ramberg C<mramberg@cpan.org>
867             Christian Hansen
868             Yoshinori Sano
869             Jos Boumans (kane@cpan.org)
870             Michiel Ootjers (michiel@cpan.org)
871              
872             =head1 AUTHORS
873              
874             Original Author: Sergey Nosenko (darknos@cpan.org)
875              
876             Actual Maintainer: Jose Luis Martinez Torres JLMARTIN (jlmartinez@capside.com)
877              
878             L<http://code.google.com/p/catalyst-server-jsonrpc>
879              
880             =head1 BUG REPORTS
881              
882             Please submit all bugs regarding C<Catalyst::Plugin::Server::JSONRPC> to
883             C<http://code.google.com/p/catalyst-server-jsonrpc/issues/entry>
884              
885             =head1 LICENSE
886              
887             This library is free software, you can redistribute it and/or modify
888             it under the same terms as Perl itself.
889              
890             =cut