File Coverage

blib/lib/JSON/RPC2/AnyEvent/Server/Handle.pm
Criterion Covered Total %
statement 63 71 88.7
branch 6 20 30.0
condition 2 6 33.3
subroutine 19 20 95.0
pod 2 3 66.6
total 92 120 76.6


line stmt bran cond sub pod time code
1             package JSON::RPC2::AnyEvent::Server::Handle;
2 2     2   89626 use 5.010;
  2         8  
  2         186  
3 2     2   13 use strict;
  2         5  
  2         71  
4 2     2   12 use warnings;
  2         2  
  2         124  
5              
6             our $VERSION = "0.02";
7              
8 2     2   2304 use AnyEvent::Handle;
  2         36875  
  2         75  
9 2     2   18 use Carp qw(croak);
  2         4  
  2         136  
10 2     2   12 use Errno ();
  2         4  
  2         35  
11 2     2   1249 use JSON;
  2         17394  
  2         50  
12 2     2   351 use Scalar::Util qw(blessed reftype openhandle);
  2         3  
  2         211  
13              
14 2     2   738 use JSON::RPC2::AnyEvent::Server;
  2         4  
  2         67  
15 2     2   12 use JSON::RPC2::AnyEvent::Constants qw(ERR_PARSE_ERROR);
  2         3  
  2         1207  
16              
17              
18             sub new {
19 1     1 1 2 my ($class, $srv, $hdl) = @_;
20            
21 1 50 33     22 croak "Not an JSON::RPC2::AnyEvent::Server object: $srv" unless blessed $srv && $srv->isa('JSON::RPC2::AnyEvent::Server');
22            
23 1 50 33     6 unless ( blessed $hdl && $hdl->isa('AnyEvent::Handle') ) {
24 1 50       8 $hdl = openhandle $hdl or croak "Neither AnyEvent::Handle nor open filehandle: $hdl";
25 1         6 $hdl = AnyEvent::Handle->new(fh => $hdl);
26             }
27            
28 1         47 my $self = bless {
29             hdl => $hdl,
30             srv => $srv,
31             }, $class;
32            
33             $hdl->on_read(sub{
34             shift->push_read(json => sub{
35 2         107 my ($h, $json) = @_;
36             $self->{srv}->dispatch($json)->cb(sub{
37 2         26 my $res = shift->recv;
38 2 50       24 $h->push_write(json => $res) if defined $res;
39 2         9 });
40 2     2   126 });
41 1         8 });
42            
43             $hdl->on_eof(sub{
44 1     1   3142 my $on_end = $self->{on_end};
45 1         6 $self->destroy;
46 1 50       11 $on_end->($self) if $on_end;
47 1         37 });
48            
49             $hdl->on_error(sub{
50 0     0   0 my ($h, $fatal, $msg) = @_;
51 0 0       0 if ( $! == Errno::EBADMSG ) { # JSON Parse error
    0          
52 0         0 my $res = JSON::RPC2::AnyEvent::_make_error_response(undef, ERR_PARSE_ERROR, 'Parse error');
53 0         0 $h->push_write(json => $res);
54             } elsif ( $self->{on_error} ){
55 0         0 $self->{on_error}->($self, $fatal, $msg);
56 0 0       0 $self->destroy if $fatal;
57             } else {
58 0 0       0 $self->destroy if $fatal;
59 0         0 croak "JSON::RPC2::AnyEvent::Handle uncaught error: $msg";
60             }
61 1         10 });
62            
63 1         5 $self;
64             }
65              
66              
67             sub JSON::RPC2::AnyEvent::Server::dispatch_fh{
68 1     1 0 1007515 my ($self, $fh) = @_;
69 1         10 __PACKAGE__->new($self, $fh);
70             }
71              
72              
73             # Create on_xxx methods
74             for my $name ( qw/ on_end on_error / ) {
75 2     2   12 no strict 'refs';
  2         3  
  2         460  
76             *$name = sub {
77 2     2   14 my ($self, $code) = @_;
78 2 50       9 reftype $code eq 'CODE' or croak "coderef must be specified";
79 2         10 $self->{$name} = $code;
80             };
81             }
82              
83              
84             # This DESTROY-pattern originates from AnyEvent::Handle code.
85             sub DESTROY {
86 1     1   3 my ($self) = @_;
87 1         12 $self->{hdl}->destroy;
88             }
89              
90             sub destroy {
91 1     1 1 2 my ($self) = @_;
92 1         5 $self->DESTROY;
93 1         35 %$self = ();
94 1         8 bless $self, "JSON::RPC2::AnyEvent::Server::Handle::destroyed";
95             }
96              
97 1     1   13 sub JSON::RPC2::AnyEvent::Server::Handle::destroyed::AUTOLOAD {
98             #nop
99             }
100              
101              
102             1;
103             __END__
104              
105             =encoding utf-8
106              
107             =head1 NAME
108              
109             JSON::RPC2::AnyEvent::Server::Handle - dispatch JSON-RPC requests comming from file-handle to JSON::RPC2::AnyEvent::Server
110              
111             =head1 SYNOPSIS
112              
113             use AnyEvent::Socket;
114             use JSON::RPC2::AnyEvent::Server::Handle; # Add `dispatch_fh' method in JSON::RPC2::AnyEvent::Server
115            
116             my $srv = JSON::RPC2::AnyEvent::Server->(
117             echo => sub{
118             my ($cv, $args) = @_;
119             $cv->send($args);
120             }
121             );
122            
123             my $w = tcp_server undef, 8080, sub {
124             my ($fh, $host, $port) = @_;
125             my $hdl = $srv->dispatch_fh($fh); # equivalent to JSON::RPC2::AnyEvent::Server::Handle->new($srv, $fh)
126             $hdl->on_end(sub{
127             my $h = shift; # JSON::RPC2::AnyEvent::Server::Handle
128             # underlying fh is already closed here
129             $h->destroy;
130             undef $hdl;
131             });
132             $hdl->on_error(sub{
133             my ($h, $fatal, $message) = @_;
134             warn $message;
135             $h->destroy if $fatal;
136             undef $hdl;
137             });
138             };
139              
140             =head1 DESCRIPTION
141              
142             JSON::RPC2::AnyEvent::Server::Handle is AnyEvent::Handle adapter for JSON::RPC2::AnyEvent::Server.
143              
144              
145             =head1 INTERFACE
146              
147             =head2 C<CLASS-E<gt>new($srv, $fh)> -> C<$handle>
148              
149             =head2 C<$srv-E<gt>dispatch_fh($fh)> -> C<$handle>
150              
151             Connect C<$fh> to C<$srv> and returns a JSON::RPC2::AnyEvent::Handle object.
152             The object dispatches coming requests to C<$srv> and sends back returned response to C<$fh>.
153              
154             This module adds C<dispatch_fh> method in JSON::RPC2::AnyEvent::Server, which can be used as a shortcut of C<new>.
155              
156             =over
157              
158             =item C<$srv>: JSON::RPC2::AnyEvent::Server
159              
160             JSON::RPC2::AnyEvent::Server object to connect.
161              
162             =item C<$fh>: AnyEvent::Handle or file-handle
163              
164             File handle to be connected.
165              
166             =item C<$handle>: JSON::RPC2::AnyEvent::Server::Handle
167              
168             New JSON::RPC2::AnyEvent::Server::Handle object.
169              
170             =back
171              
172             =head2 C<$self-E<gt>on_end(sub{ my($self) = @_; ... })>
173              
174             Registers callback called when the underlying file handle successfully reaches EOF.
175              
176             =head2 C<$self-E<gt>on_error(sub{ my($self, $fatal, $message) = @_; ... })>
177              
178             Registers callback called when an error occurs during comminication.
179              
180             =head2 C<$self-E<gt>destroy>
181              
182             Manually destroys this object.
183              
184              
185             =head1 LICENSE
186              
187             Copyright (C) Daisuke (yet another) Maki.
188              
189             This library is free software; you can redistribute it and/or modify
190             it under the same terms as Perl itself.
191              
192             =head1 AUTHOR
193              
194             Daisuke (yet another) Maki E<lt>maki.daisuke@gmail.comE<gt>
195              
196             =cut