File Coverage

blib/lib/RPC/Serialized/Server.pm
Criterion Covered Total %
statement 135 160 84.3
branch 40 66 60.6
condition 5 12 41.6
subroutine 21 24 87.5
pod 3 14 21.4
total 204 276 73.9


line stmt bran cond sub pod time code
1             package RPC::Serialized::Server;
2             {
3             $RPC::Serialized::Server::VERSION = '1.123630';
4             }
5              
6 16     16   9052 use strict;
  16         37  
  16         730  
7 16     16   84 use warnings FATAL => 'all';
  16         30  
  16         782  
8              
9 16     16   84 use base 'RPC::Serialized';
  16         23  
  16         17824  
10              
11 16     16   166 use UNIVERSAL;
  16         33  
  16         121  
12 16     16   459 use RPC::Serialized::Config;
  16         34  
  16         155  
13 16     16   8052 use RPC::Serialized::Exceptions;
  16         42  
  16         86  
14 16     16   11524 use RPC::Serialized::AuthzHandler;
  16         39  
  16         190  
15              
16             __PACKAGE__->mk_ro_accessors(qw/
17             timeout
18             /);
19             __PACKAGE__->mk_accessors(qw/
20             trace handler_namespaces args_suppress_log callbacks
21             /);
22              
23             sub new {
24 7     7 1 24819 my $class = shift;
25 7         480 my $params = RPC::Serialized::Config->parse(@_);
26              
27 7         4903 my $ns = $params->rpc_serialized->{handler_namespaces};
28 7 100       422 $params->rpc_serialized->{handler_namespaces} =
    50          
29             (!defined $ns ? [] : (!ref $ns ? [$ns] : $ns));
30              
31 7         412 my $self = $class->SUPER::new($params);
32              
33 7 50       1171 if ($self->trace) {
34 0         0 eval { require Log::Dispatch::Syslog };
  0         0  
35              
36 0 0       0 if ($@) {
37 0         0 throw_app "Failed to load Log::Dispatch but trace is on: $@";
38             }
39             else {
40 0         0 $self->trace( Log::Dispatch::Syslog->new(
41             $params->log_dispatch_syslog,
42             ));
43             }
44             }
45              
46             # FIXME erm, should these be accessors?
47 7 50       146 $self->{HANDLER} = $params->rpc_serialized->{handlers}
48             if exists $params->rpc_serialized->{handlers};
49 7         323 $self->{AUTHZ_HANDLER} = RPC::Serialized::AuthzHandler->new;
50 7 50       40 $self->{CALLBACKS} = $params->rpc_serialized->{callbacks}
51             if exists $params->rpc_serialized->{callbacks};
52              
53 7         435 return $self;
54             }
55              
56             sub log {
57 38     38 1 78 my $self = shift;
58 38 50       151 return unless $self->trace;
59              
60 0         0 ( my $log = $self->ds->raw_serialize(@_) ) =~ s/^/[$$] /gm;
61 0         0 $self->trace->log( level => $self->trace->{min_level}, message => $log);
62             }
63              
64             sub log_call {
65 19     19 0 46 my $self = shift;
66 19         53 my ( $call, $args ) = @_;
67              
68             # strip suppressed (sensitive) arguments, e.g. password fields
69 19 50 66     32 if (scalar @{$args} % 2 == 0
  19   33     189  
70             and exists $self->args_suppress_log->{$call}
71             and ref $self->args_suppress_log->{$call} eq ref []) {
72              
73 0         0 my %args = @{$args};
  0         0  
74 0         0 foreach ( @{ $self->args_suppress_log->{$call} } ) {
  0         0  
75 0 0       0 if ( exists $args{$_} ) {
76 0         0 $args{$_} = '[suppressed]';
77             }
78             }
79 0         0 $args = [%args];
80             }
81              
82 19         424 $self->log( { CALL => $call, SUBJECT => $self->subject, ARGS => $args } );
83             }
84              
85             sub log_response {
86 19     19 0 38 my $self = shift;
87 19         33 my $response = shift;
88 19         66 $self->log($response);
89             }
90              
91             sub handler {
92 25     25 1 85 my $self = shift;
93 25         52 my $call = shift;
94              
95 25 100       86 if (@_) {
96 6         47 $self->{HANDLER}->{$call} = shift;
97             }
98              
99 25 100       149 return $self->{HANDLER}->{$call}
100             if exists $self->{HANDLER}->{$call};
101 10         37 return;
102             }
103              
104             sub authz_handler {
105 13     13 0 28 my $self = shift;
106              
107 13 50       49 if (@_) {
108 0         0 my $handler = shift;
109              
110 0 0       0 throw_app 'Not a RPC::Serialized::AuthzHandler'
111             unless UNIVERSAL::isa( $handler, 'RPC::Serialized::AuthzHandler' );
112 0         0 $self->{AUTHZ_HANDLER} = $handler;
113             }
114              
115 13         60 return $self->{AUTHZ_HANDLER};
116             }
117              
118             sub recv {
119 24     24 0 2402 my $self = shift;
120 24 100       6208 my ($data, @token) = $self->SUPER::recv or return;
121              
122 19         56 my $call = $data->{CALL};
123 19 50 33     123 throw_proto 'Invalid or missing CALL'
124             unless $call and not ref($call);
125              
126 19         176 my $args = $data->{ARGS};
127 19 50 33     164 throw_proto 'Invalid or missing ARGS'
128             unless $args and ref($args) eq 'ARRAY';
129              
130 19         163 return ( $call, $args, @token );
131             }
132              
133             sub subject {
134 32     32 0 59 my $self = shift;
135 32         249 return undef;
136             }
137              
138             sub authorize {
139 13     13 0 28 my $self = shift;
140 13         28 my $call = shift;
141 13         175 my $target = shift;
142 13         666 $self->authz_handler->check_authz( $self->subject, $call, $target );
143             }
144              
145             sub dispatch {
146 19     19 0 34 my $self = shift;
147 19         40 my $call = shift;
148 19         28 my $args = shift;
149              
150 19         34 my $hc = undef;
151 19 100       68 if ($hc = $self->handler($call)) {
152 9 50       965 eval "require $hc"
153             or throw_system "Failed to load $hc: $@";
154             }
155             else {
156 10         84 $call = quotemeta($call);
157 10 50       53 throw_app "Cannot search for invalid name: $call"
158             if $call =~ m/\W/;
159              
160 10         33 (my $name = $call) =~ s/_([a-z])/::\u$1/g;
161 10         30 $name = ucfirst $name;
162              
163 10         18 foreach my $ns (@{ $self->handler_namespaces }) {
  10         132  
164 12 100       1626 eval "require ${ns}::${name}" or next;
165              
166             # install the handler class we have just found
167 4         434 $hc = "${ns}::$name";
168 4         30 $self->handler($call, $hc);
169 4         19 last;
170             }
171             }
172              
173 19 100       7169 throw_app "No handler for $call"
174             if !defined $hc;
175              
176 13 50       128 throw_app "$hc not a RPC::Serialized::Handler"
177             unless $hc->isa('RPC::Serialized::Handler');
178              
179 13 50       95 $self->authorize( $call, $hc->target(@$args) )
180             or throw_authz "Permission denied";
181              
182 13 100       234 if ($self->callbacks->{pre_handler_argument_filter}) {
183 2         24 eval {
184 2         12 $args = [ $self->callbacks->{pre_handler_argument_filter}->(
185             { call => $call, server => $self },
186             @$args) ];
187             };
188 2 50       41 if ($@) {
189 0         0 throw_app sprintf("Callback '%s' for call '%s' returned %s"
190             , 'pre_handler_argument_filter'
191             , $call
192             , $@);
193             }
194             }
195            
196 13         200 return { RESPONSE => $hc->invoke(@$args) };
197             }
198              
199             sub exception {
200 6     6 0 13 my $self = shift;
201 6         16 my $err = shift;
202              
203 6         12 my $exception;
204 6 50       40 if ( UNIVERSAL::isa( $err, 'RPC::Serialized::X' ) ) {
205 6         91 $exception = {
206             CLASS => ref($err),
207             MESSAGE => $err->message
208             };
209             }
210             else {
211 0         0 $exception = {
212             CLASS => 'RPC::Serialized::X',
213             MESSAGE => "$err"
214             };
215             }
216              
217 6         67 return { EXCEPTION => $exception };
218             }
219              
220             sub process {
221 5     5 0 28363 my $self = shift;
222              
223 5         16 my $alarm_bak = 0;
224 5         24 my @token_bak = ();
225              
226 5         21 while ( 1 ) {
227 24         342 my ($response, @token);
228              
229 24         56 eval {
230 24     0   851 local $SIG{ALRM} = sub { die "Timeout on Receive\n" };
  0         0  
231 24         126 $alarm_bak = alarm $self->timeout;
232 24         500 (my ($call, $args), @token) = ($self->recv);
233 24         169 alarm $alarm_bak;
234              
235 24 100       1448 if ($call) {
236 19         410 $self->log_call( $call, $args );
237              
238 19     0   624 local $SIG{ALRM} = sub { die "Timeout on Dispatch\n" };
  0         0  
239 19         100 $alarm_bak = alarm $self->timeout;
240 19         305 $response = $self->dispatch( $call, $args );
241 13         390 alarm $alarm_bak;
242             }
243             };
244 24 100       21220 if ($@) {
245 6         82 alarm $alarm_bak;
246 6         31 $response = $self->exception($@);
247             }
248              
249 24 100       81 last unless $response;
250 19         77 $self->log_response($response);
251              
252             # use same serializer for response as on received msg
253 19 50       264 @token_bak = $self->set_token(@token)
254             if !$self->debug;
255              
256 19         42 eval {
257 19     0   5275 local $SIG{ALRM} = sub { die "Timeout on Send\n" };
  0         0  
258 19         97 $alarm_bak = alarm $self->timeout;
259 19         464 $self->send($response);
260 19         13138 alarm $alarm_bak;
261             };
262 19 50       69 if ($@) {
263 0         0 alarm $alarm_bak;
264 0 0       0 $self->restore_token(@token_bak) if !$self->debug;
265 0         0 throw_system $@; # likely caught outside of RPC::Serialized
266             }
267              
268             # restore our default serializer
269 19 50       80 $self->restore_token(@token_bak) if !$self->debug;
270             }
271              
272 5         51 alarm $alarm_bak;
273             }
274              
275             sub restore_token {
276 38     38 0 359 my $self = shift;
277 38         96 my ($serializer, $cipher, $digester, $encoding, $compressor) = @_;
278              
279 38         117 $self->ds->serializer($serializer);
280 38         2896 $self->ds->cipher($cipher);
281 38         792 $self->ds->digester($digester);
282 38         613 $self->ds->encoding($encoding);
283 38         849 $self->ds->compressor($compressor);
284             }
285              
286             sub set_token {
287 19     19 0 350 my $self = shift;
288 19         55 my ($serializer, $cipher, $digester, $encoding, $compressor) = @_;
289              
290 19         82 my @retval = (
291             $self->ds->serializer,
292             $self->ds->cipher,
293             $self->ds->digester,
294             $self->ds->encoding,
295             $self->ds->compressor,
296             );
297              
298 19         1475 $self->restore_token(@_);
299 19         331 return @retval;
300             }
301              
302             1;
303