File Coverage

blib/lib/JSON/RPC2/Server.pm
Criterion Covered Total %
statement 124 124 100.0
branch 52 52 100.0
condition 12 12 100.0
subroutine 22 22 100.0
pod 6 6 100.0
total 216 216 100.0


line stmt bran cond sub pod time code
1             package JSON::RPC2::Server;
2              
3 15     15   111210 use warnings;
  15         33  
  15         730  
4 15     15   90 use strict;
  15         30  
  15         562  
5 15     15   84 use Carp;
  15         123  
  15         1352  
6              
7 15     15   1951 use version; our $VERSION = qv('1.0.0'); # update Changes & README
  15         5966  
  15         166  
8              
9             # update DEPENDENCIES in POD & Makefile.PL & README
10 15     15   4211 use JSON::XS;
  15         23511  
  15         1036  
11              
12 15     15   93 use constant ERR_PARSE => -32700;
  15         25  
  15         1375  
13 15     15   79 use constant ERR_REQ => -32600;
  15         37  
  15         778  
14 15     15   94 use constant ERR_METHOD => -32601;
  15         34  
  15         772  
15 15     15   77 use constant ERR_PARAMS => -32602;
  15         49  
  15         24353  
16              
17              
18             sub new {
19 5     5 1 60 my ($class) = @_;
20 5         23 my $self = {
21             method => {},
22             };
23 5         24 return bless $self, $class;
24             }
25              
26             sub register {
27 11     11 1 1534 my ($self, $name, $cb) = @_;
28 11         53 $self->{method}{ $name } = [ $cb, 1, 0 ];
29 11         29 return;
30             }
31              
32             sub register_named {
33 6     6 1 43 my ($self, $name, $cb) = @_;
34 6         60 $self->{method}{ $name } = [ $cb, 1, 1 ];
35 6         14 return;
36             }
37              
38             sub register_nb {
39 5     5 1 26 my ($self, $name, $cb) = @_;
40 5         16 $self->{method}{ $name } = [ $cb, 0, 0 ];
41 5         10 return;
42             }
43              
44             sub register_named_nb {
45 5     5 1 26 my ($self, $name, $cb) = @_;
46 5         18 $self->{method}{ $name } = [ $cb, 0, 1 ];
47 5         9 return;
48             }
49              
50             sub execute {
51 75     75 1 38775 my ($self, $json, $cb) = @_;
52 75 100       261 croak 'require 2 params' if 1+2 != @_;
53 72 100       245 croak 'second param must be callback' if ref $cb ne 'CODE';
54              
55 68         104 undef $@;
56 68 100       210 my $request = ref $json ? $json : eval { decode_json($json) };
  64         484  
57 68 100       209 if ($@) {
58 5         13 return _error($cb, undef, ERR_PARSE, 'Parse error.');
59             }
60 63 100       172 if (ref $request eq 'HASH') {
61 54         158 return $self->_execute($request, $cb);
62             }
63 9 100       21 if (ref $request ne 'ARRAY') {
64 2         6 return _error($cb, undef, ERR_REQ, 'Invalid Request: expect Array or Object.');
65             }
66 7 100       10 if (!@{$request}) {
  7         20  
67 2         5 return _error($cb, undef, ERR_REQ, 'Invalid Request: empty Array.');
68             }
69              
70 5         6 my $pending = @{$request};
  5         8  
71 5         5 my @responses;
72             my $cb_acc = sub {
73 21     21   39 my ($json_response) = @_;
74 21 100       56 if ($json_response) {
75 13         22 push @responses, $json_response;
76             }
77 21 100       198 if (!--$pending) {
78 5 100       15 if (@responses) {
79 4         33 $cb->( '[' . join(q{,}, @responses) . ']' );
80             } else {
81 1         4 $cb->( q{} );
82             }
83             }
84 21         123 return;
85 5         24 };
86 5         8 for (@{$request}) {
  5         10  
87 21         47 $self->_execute($_, $cb_acc);
88             }
89              
90 5         55 return;
91             }
92              
93             sub _execute {
94 75     75   188 my ($self, $request, $cb) = @_;
95              
96 75         274 my $error = \&_error;
97 75         115 my $done = \&_done;
98              
99             # jsonrpc =>
100 75 100 100     1244 if (!defined $request->{jsonrpc} || ref $request->{jsonrpc} || $request->{jsonrpc} ne '2.0') {
      100        
101 13         26 return $error->($cb, undef, ERR_REQ, 'Invalid Request: expect {jsonrpc}="2.0".');
102             }
103              
104             # id =>
105 62         84 my $id;
106 62 100       149 if (exists $request->{id}) {
107             # Request
108 48 100       111 if (ref $request->{id}) {
109 4         9 return $error->($cb, undef, ERR_REQ, 'Invalid Request: expect {id} is scalar.');
110             }
111 44         303 $id = $request->{id};
112             }
113              
114             # method =>
115 58 100 100     286 if (!defined $request->{method} || ref $request->{method}) {
116 5         11 return $error->($cb, $id, ERR_REQ, 'Invalid Request: expect {method} is String.');
117             }
118 53         135 my $handler = $self->{method}{ $request->{method} };
119 53 100       117 if (!$handler) {
120 3         7 return $error->($cb, $id, ERR_METHOD, 'Method not found.');
121             }
122 50         61 my ($method, $is_blocking, $is_named) = @{$handler};
  50         104  
123              
124             # params =>
125 50 100       129 if (!exists $request->{params}) {
126 2 100       10 $request->{params} = $is_named ? {} : [];
127             }
128 50 100 100     240 if (ref $request->{params} ne 'ARRAY' && ref $request->{params} ne 'HASH') {
129 5         13 return $error->($cb, $id, ERR_REQ, 'Invalid Request: expect {params} is Array or Object.');
130             }
131 45 100       165 if (ref $request->{params} ne ($is_named ? 'HASH' : 'ARRAY')) {
    100          
132 15 100       71 return $error->($cb, $id, ERR_PARAMS, 'This method expect '.($is_named ? 'named' : 'positional').' params.');
133             }
134 30 100       48 my @params = $is_named ? %{ $request->{params} } : @{ $request->{params} };
  13         50  
  17         53  
135              
136             # id => (continue)
137 30 100       83 if (!exists $request->{id}) {
138             # Notification
139 10         18 $error = \&_nothing;
140 10         17 $done = \&_nothing;
141             }
142              
143             # execute
144 30 100       63 if ($is_blocking) {
145 21         68 my @returns = $method->( @params );
146 21         155 $done->($cb, $id, \@returns);
147             }
148             else {
149 9     9   130 my $cb_done = sub { $done->($cb, $id, \@_) };
  9         39859  
150 9         36 $method->( $cb_done, @params );
151             }
152 30         329 return;
153             }
154              
155             sub _done {
156 20     20   49 my ($cb, $id, $returns) = @_;
157 20         34 my ($result, $code, $msg, $data) = @{$returns};
  20         300  
158 20 100       75 if (defined $code) {
159 9         274 return _error($cb, $id, $code, $msg, $data);
160             }
161 11         81 return _result($cb, $id, $result);
162             }
163              
164             sub _error {
165 63     63   116 my ($cb, $id, $code, $message, $data) = @_;
166 63 100       776 $cb->( encode_json({
167             jsonrpc => '2.0',
168             id => $id,
169             error => {
170             code => $code,
171             message => $message,
172             (defined $data ? ( data => $data ) : ()),
173             },
174             }) );
175 63         792 return;
176             }
177              
178             sub _result {
179 11     11   25 my ($cb, $id, $result) = @_;
180 11         139 $cb->( encode_json({
181             jsonrpc => '2.0',
182             id => $id,
183             result => $result,
184             }) );
185 11         103 return;
186             }
187              
188             sub _nothing {
189 10     10   22 my ($cb) = @_;
190 10         29 $cb->( q{} );
191 10         77 return;
192             }
193              
194              
195             1; # Magic true value required at end of module
196             __END__
197              
198             =encoding utf8
199              
200             =head1 NAME
201              
202             JSON::RPC2::Server - Transport-independent json-rpc 2.0 server
203              
204              
205             =head1 SYNOPSIS
206              
207             use JSON::RPC2::Server;
208              
209             my $rpcsrv = JSON::RPC2::Server->new();
210              
211             $rpcsrv->register('func1', \&func1);
212             $rpcsrv->register_nb('func2', \&func2);
213             $rpcsrv->register_named('func3', \&func3);
214             $rpcsrv->register_named_nb('func4', \&func4);
215              
216             # receive remote request in $json_request somehow, then:
217             $rpcsrv->execute( $json_request, \&send_response );
218              
219             sub send_response {
220             my ($json_response) = @_;
221             # send $json_response somehow
222             }
223              
224             sub func1 {
225             my (@remote_params) = @_;
226             if (success) {
227             return ($result);
228             } else {
229             return (undef, $err_code, $err_message);
230             }
231             }
232              
233             sub func2 {
234             my ($callback, @remote_params) = @_;
235             # setup some event to call func2_finished($callback) later
236             }
237             sub func2_finished {
238             my ($callback) = @_;
239             if (success) {
240             $callback->($result);
241             } else {
242             $callback->(undef, $err_code, $err_message);
243             }
244             return;
245             }
246              
247             sub func3 {
248             my (%remote_params) = @_;
249             # rest the same as in func1
250             }
251              
252             sub func4 {
253             my ($callback, %remote_params) = @_;
254             # rest the same as in func2
255             }
256              
257             #
258             # EXAMPLE of simple blocking STDIN-STDOUT server
259             #
260              
261             my $rpcsrv = JSON::RPC2::Server->new();
262             $rpcsrv->register('method1', \&method1);
263             $rpcsrv->register('method2', \&method2);
264             while (<STDIN>) {
265             chomp;
266             $rpcsrv->execute($_, sub { printf "%s\n", @_ });
267             }
268             sub method1 {
269             return { my_params => \@_ };
270             }
271             sub method2 {
272             return (undef, 0, "don't call me please");
273             }
274              
275             =head1 DESCRIPTION
276              
277             Transport-independent implementation of json-rpc 2.0 server.
278             Server methods can be blocking (simpler) or non-blocking (useful if
279             method have to do some slow tasks like another RPC or I/O which can
280             be done in non-blocking way - this way several methods can be executing
281             in parallel on server).
282              
283              
284             =head1 INTERFACE
285              
286             =over
287              
288             =item new()
289              
290             Create and return new server object, which can be used to register and
291             execute user methods.
292              
293             =item register( $rpc_method_name, \&method_handler )
294              
295             =item register_named( $rpc_method_name, \&method_handler )
296              
297             Register $rpc_method_name as allowed method name for remote procedure call
298             and set \&method_handler as BLOCKING handler for that method.
299              
300             If there already was some handler set (using register() or
301             register_named() or register_nb() or register_named_nb()) for that
302             $rpc_method_name - it will be replaced by \&method_handler.
303              
304             While processing request to $rpc_method_name user handler will be called
305             with parameters provided by remote side (as ARRAY for register() or HASH
306             for register_named()), and should return it result as list with 4
307             elements:
308              
309             ($result, $code, $message, $data) = method_handler(@remote_params);
310             ($result, $code, $message, $data) = method_handler(%remote_params);
311              
312             $result scalar or complex structure if method call success
313             $code error code (integer, > -32600) if method call failed
314             $message error message (string) if message call failed
315             $data optional scalar with additional error-related data
316              
317             If $code is defined then $result shouldn't be defined; $message required
318             only if $code defined.
319              
320             Return nothing.
321              
322             =item register_nb( $rpc_method_name, \&nb_method_handler )
323              
324             =item register_named_nb( $rpc_method_name, \&nb_method_handler )
325              
326             Register $rpc_method_name as allowed method name for remote procedure call
327             and set \&method_handler as NON-BLOCKING handler for that method.
328              
329             If there already was some handler set (using register() or
330             register_named() or register_nb() or register_named_nb()) for that
331             $rpc_method_name - it will be replaced by \&method_handler.
332              
333             While processing request to $rpc_method_name user handler will be called
334             with callback needed to return result in first parameter and parameters
335             provided by remote side as next parameters (as ARRAY for register_nb() or
336             HASH for register_named_nb()), and should call provided callback with list
337             with 4 elements when done:
338              
339             nb_method_handler($callback, @remote_params);
340             nb_method_handler($callback, %remote_params);
341              
342             # somewhere in that method handlers:
343             $callback->($result, $code, $message, $data);
344             return;
345              
346             Meaning of ($result, $code, $message, $data) is same as documented in
347             register() above.
348              
349             Return nothing.
350              
351             =item execute( $json_request, $callback )
352              
353             The $json_request can be either JSON string or ARRAYREF/HASHREF (useful
354             with C<< $handle->push_read(json => sub{...}) >> from L<AnyEvent::Handle>).
355              
356             Parse $json_request and execute registered user handlers. Reply will be
357             sent into $callback, when ready:
358              
359             $callback->( $json_response );
360              
361             The $callback will be always executed after finishing processing
362             $json_request - even if request type was "notification" (in this case
363             $json_response will be an empty string).
364              
365             Return nothing.
366              
367             =back
368              
369              
370             =head1 DIAGNOSTICS
371              
372             None.
373              
374              
375             =head1 CONFIGURATION AND ENVIRONMENT
376              
377             JSON::RPC2::Server requires no configuration files or environment variables.
378              
379              
380             =head1 DEPENDENCIES
381              
382             JSON::XS
383              
384              
385             =head1 INCOMPATIBILITIES
386              
387             None reported.
388              
389              
390             =head1 BUGS AND LIMITATIONS
391              
392             No bugs have been reported.
393              
394             Please report any bugs or feature requests to
395             C<bug-json-rpc2-server@rt.cpan.org>, or through the web interface at
396             L<http://rt.cpan.org>.
397              
398              
399             =head1 AUTHOR
400              
401             Alex Efros C<< <powerman-asdf@ya.ru> >>
402              
403              
404             =head1 LICENSE AND COPYRIGHT
405              
406             Copyright (c) 2009,2014, Alex Efros C<< <powerman-asdf@ya.ru> >>. All rights reserved.
407              
408             This module is free software; you can redistribute it and/or
409             modify it under the same terms as Perl itself. See L<perlartistic>.
410              
411              
412             =head1 DISCLAIMER OF WARRANTY
413              
414             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
415             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
416             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
417             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
418             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
419             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
420             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
421             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
422             NECESSARY SERVICING, REPAIR, OR CORRECTION.
423              
424             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
425             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
426             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
427             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
428             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
429             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
430             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
431             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
432             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
433             SUCH DAMAGES.