File Coverage

blib/lib/JSON/RPC2/Server.pm
Criterion Covered Total %
statement 126 126 100.0
branch 52 52 100.0
condition 12 12 100.0
subroutine 23 23 100.0
pod 6 6 100.0
total 219 219 100.0


line stmt bran cond sub pod time code
1             package JSON::RPC2::Server;
2 14     14   33151 use 5.010001;
  14         31  
3 14     14   54 use warnings;
  14         80  
  14         319  
4 14     14   46 use strict;
  14         12  
  14         226  
5 14     14   1070 use utf8;
  14         27  
  14         59  
6 14     14   246 use Carp;
  14         14  
  14         897  
7              
8             our $VERSION = 'v2.1.1';
9              
10 14     14   785 use JSON::MaybeXS;
  14         11613  
  14         656  
11              
12 14     14   54 use constant ERR_PARSE => -32700;
  14         26  
  14         841  
13 14     14   49 use constant ERR_REQ => -32600;
  14         16  
  14         552  
14 14     14   48 use constant ERR_METHOD => -32601;
  14         17  
  14         518  
15 14     14   59 use constant ERR_PARAMS => -32602;
  14         17  
  14         16144  
16              
17              
18             sub new {
19 5     5 1 58 my ($class) = @_;
20 5         16 my $self = {
21             method => {},
22             };
23 5         20 return bless $self, $class;
24             }
25              
26             sub register {
27 11     11 1 2254 my ($self, $name, $cb) = @_;
28 11         36 $self->{method}{ $name } = [ $cb, 1, 0 ];
29 11         18 return;
30             }
31              
32             sub register_named {
33 6     6 1 29 my ($self, $name, $cb) = @_;
34 6         9 $self->{method}{ $name } = [ $cb, 1, 1 ];
35 6         6 return;
36             }
37              
38             sub register_nb {
39 5     5 1 17 my ($self, $name, $cb) = @_;
40 5         9 $self->{method}{ $name } = [ $cb, 0, 0 ];
41 5         5 return;
42             }
43              
44             sub register_named_nb {
45 5     5 1 15 my ($self, $name, $cb) = @_;
46 5         11 $self->{method}{ $name } = [ $cb, 0, 1 ];
47 5         4 return;
48             }
49              
50             sub execute {
51 75     75 1 31785 my ($self, $json, $cb) = @_;
52 75 100       209 croak 'require 2 params' if 1+2 != @_;
53 72 100       180 croak 'second param must be callback' if ref $cb ne 'CODE';
54              
55 68         76 undef $@;
56 68 100       121 my $request = ref $json ? $json : eval { decode_json($json) };
  64         397  
57 68 100       128 if ($@) {
58 5         13 return _error($cb, undef, ERR_PARSE, 'Parse error.');
59             }
60 63 100       132 if (ref $request eq 'HASH') {
61 54         94 return $self->_execute($request, $cb);
62             }
63 9 100       19 if (ref $request ne 'ARRAY') {
64 2         4 return _error($cb, undef, ERR_REQ, 'Invalid Request: expect Array or Object.');
65             }
66 7 100       8 if (!@{$request}) {
  7         14  
67 2         5 return _error($cb, undef, ERR_REQ, 'Invalid Request: empty Array.');
68             }
69              
70 5         4 my $pending = @{$request};
  5         5  
71 5         4 my @responses;
72             my $cb_acc = sub {
73 21     21   22 my ($json_response) = @_;
74 21 100       36 if ($json_response) {
75 13         17 push @responses, $json_response;
76             }
77 21 100       38 if (!--$pending) {
78 5 100       9 if (@responses) {
79 4         25 $cb->( '[' . join(q{,}, @responses) . ']' );
80             } else {
81 1         2 $cb->( q{} );
82             }
83             }
84 21         90 return;
85 5         15 };
86 5         5 for (@{$request}) {
  5         8  
87 21         26 $self->_execute($_, $cb_acc);
88             }
89              
90 5         31 return;
91             }
92              
93             sub _execute {
94 75     75   81 my ($self, $request, $cb) = @_;
95              
96 75         78 my $error = \&_error;
97 75         64 my $done = \&_done;
98              
99             # jsonrpc =>
100 75 100 100     446 if (!defined $request->{jsonrpc} || ref $request->{jsonrpc} || $request->{jsonrpc} ne '2.0') {
      100        
101 13         17 return $error->($cb, undef, ERR_REQ, 'Invalid Request: expect {jsonrpc}="2.0".');
102             }
103              
104             # id =>
105 62         50 my $id;
106 62 100       93 if (exists $request->{id}) {
107             # Request
108 48 100       80 if (ref $request->{id}) {
109 4         8 return $error->($cb, undef, ERR_REQ, 'Invalid Request: expect {id} is scalar.');
110             }
111 44         46 $id = $request->{id};
112             }
113              
114             # method =>
115 58 100 100     214 if (!defined $request->{method} || ref $request->{method}) {
116 5         8 return $error->($cb, $id, ERR_REQ, 'Invalid Request: expect {method} is String.');
117             }
118 53         90 my $handler = $self->{method}{ $request->{method} };
119 53 100       78 if (!$handler) {
120 3         5 return $error->($cb, $id, ERR_METHOD, 'Method not found.');
121             }
122 50         43 my ($method, $is_blocking, $is_named) = @{$handler};
  50         65  
123              
124             # params =>
125 50 100       83 if (!exists $request->{params}) {
126 2 100       10 $request->{params} = $is_named ? {} : [];
127             }
128 50 100 100     156 if (ref $request->{params} ne 'ARRAY' && ref $request->{params} ne 'HASH') {
129 5         8 return $error->($cb, $id, ERR_REQ, 'Invalid Request: expect {params} is Array or Object.');
130             }
131 45 100       118 if (ref $request->{params} ne ($is_named ? 'HASH' : 'ARRAY')) {
    100          
132 15 100       49 return $error->($cb, $id, ERR_PARAMS, 'This method expect '.($is_named ? 'named' : 'positional').' params.');
133             }
134 30 100       43 my @params = $is_named ? %{ $request->{params} } : @{ $request->{params} };
  13         37  
  17         31  
135              
136             # id => (continue)
137 30 100       56 if (!exists $request->{id}) {
138             # Notification
139 10         12 $error = \&_nothing;
140 10         12 $done = \&_nothing;
141             }
142              
143             # execute
144 30 100       47 if ($is_blocking) {
145 21         51 my @returns = $method->( @params );
146 21         120 $done->($cb, $id, \@returns);
147             }
148             else {
149 9     9   29 my $cb_done = sub { $done->($cb, $id, \@_) };
  9         39833  
150 9         28 $method->( $cb_done, @params );
151             }
152 30         166 return;
153             }
154              
155             sub _done {
156 20     20   30 my ($cb, $id, $returns) = @_;
157 20         29 my ($result, $code, $msg, $data) = @{$returns};
  20         40  
158 20 100       60 if (defined $code) {
159 9         23 return _error($cb, $id, $code, $msg, $data);
160             }
161 11         51 return _result($cb, $id, $result);
162             }
163              
164             sub _error {
165 63     63   78 my ($cb, $id, $code, $message, $data) = @_;
166 63 100       533 $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         479 return;
176             }
177              
178             sub _result {
179 11     11   18 my ($cb, $id, $result) = @_;
180 11         105 $cb->( encode_json({
181             jsonrpc => '2.0',
182             id => $id,
183             result => $result,
184             }) );
185 11         93 return;
186             }
187              
188             sub _nothing {
189 10     10   13 my ($cb) = @_;
190 10         20 $cb->( q{} );
191 10         26 return;
192             }
193              
194              
195             1; # Magic true value required at end of module
196             __END__