File Coverage

blib/lib/Event/RPC/Connection.pm
Criterion Covered Total %
statement 207 274 75.5
branch 57 100 57.0
condition 15 37 40.5
subroutine 35 41 85.3
pod 0 34 0.0
total 314 486 64.6


line stmt bran cond sub pod time code
1             package Event::RPC::Connection;
2              
3 20     20   152 use strict;
  20         74  
  20         767  
4 19     19   107 use utf8;
  19         58  
  19         286  
5              
6 19     19   478 use Carp;
  19         32  
  19         3261  
7              
8 19     19   7624 use Event::RPC::Message::Negotiate;
  19         93  
  19         77325  
9              
10             #-- This can be changed for testing purposes e.g. to simulate
11             #-- old servers which don't perform any format negotitation.
12             $Event::RPC::Server::DEFAULT_MESSAGE_FORMAT = "Event::RPC::Message::Negotiate";
13              
14             my $CONNECTION_ID;
15              
16 258     258 0 1193 sub get_cid { shift->{cid} }
17 284     284 0 4257 sub get_sock { shift->{sock} }
18 845     845 0 3851 sub get_server { shift->{server} }
19              
20 214     214 0 1165 sub get_classes { shift->{server}->{classes} }
21 0     0 0 0 sub get_loaded_classes { shift->{server}->{loaded_classes} }
22 50     50 0 135 sub get_objects { shift->{server}->{objects} }
23 60     60 0 394 sub get_client_oids { shift->{client_oids} }
24              
25 155     155 0 2553 sub get_message_format { shift->{message_format} }
26 18     18 0 122 sub get_watcher { shift->{watcher} }
27 266     266 0 1393 sub get_write_watcher { shift->{write_watcher} }
28 142     142 0 366 sub get_message { shift->{message} }
29 2     2 0 20 sub get_is_authenticated { shift->{is_authenticated} }
30 0     0 0 0 sub get_auth_user { shift->{auth_user} }
31              
32 29     29 0 109 sub set_message_format { shift->{message_format} = $_[1] }
33 18     18 0 74 sub set_watcher { shift->{watcher} = $_[1] }
34 266     266 0 637 sub set_write_watcher { shift->{write_watcher} = $_[1] }
35 284     284 0 728 sub set_message { shift->{message} = $_[1] }
36 1     1 0 3 sub set_is_authenticated { shift->{is_authenticated} = $_[1] }
37 1     1 0 4 sub set_auth_user { shift->{auth_user} = $_[1] }
38              
39             sub new {
40 18     18 0 79 my $class = shift;
41 18         100 my ($server, $sock) = @_;
42              
43 18         77 my $cid = ++$CONNECTION_ID;
44              
45 18         185 my $self = bless {
46             cid => $cid,
47             sock => $sock,
48             server => $server,
49             is_authenticated => (!$server->get_auth_required),
50             auth_user => "",
51             watcher => undef,
52             write_watcher => undef,
53             message => undef,
54             client_oids => {},
55             message_format => $Event::RPC::Server::DEFAULT_MESSAGE_FORMAT,
56             }, $class;
57              
58 18 50       136 if ( $sock ) {
59 18         444 $self->log (2,
60             "Got new RPC connection. Connection ID is $cid"
61             );
62             $self->{watcher} = $self->get_server->get_loop->add_io_watcher (
63             fh => $sock,
64             poll => 'r',
65 142     142   132951 cb => sub { $self->input; 1 },
  142         1225  
66 18         147 desc => "rpc client cid=$cid",
67             );
68             }
69              
70 18         183 my $connection_hook = $server->get_connection_hook;
71 18 50       256 &$connection_hook($self, "connect") if $connection_hook;
72              
73 18         345 return $self;
74             }
75              
76             sub disconnect {
77 18     18 0 67 my $self = shift;
78              
79 18         117 $self->get_server->get_loop->del_io_watcher($self->get_watcher);
80 18 50       84 $self->get_server->get_loop->del_io_watcher($self->get_write_watcher)
81             if $self->get_write_watcher;
82 18         104 $self->set_watcher(undef);
83 18         87 $self->set_write_watcher(undef);
84 18         68 close $self->get_sock;
85              
86 18         131 my $server = $self->get_server;
87              
88 18         75 $server->set_clients_connected ( $self->get_server->get_clients_connected - 1 );
89              
90 18         61 foreach my $oid ( keys %{$self->get_client_oids} ) {
  18         203  
91 23         91 $server->deregister_object($oid);
92             }
93              
94 18         143 $self->log(2, "Client disconnected");
95              
96 18         126 my $connection_hook = $server->get_connection_hook;
97 18 50       244 &$connection_hook($self, "disconnect") if $connection_hook;
98              
99 18         127 1;
100             }
101              
102             sub get_client_object {
103 0     0 0 0 my $self = shift;
104 0         0 my ($oid) = @_;
105              
106             croak "No object registered with oid '$oid'"
107 0 0       0 unless $self->get_client_objects->{$oid};
108              
109 0         0 return $self->get_client_objects->{$oid};
110             }
111              
112             sub log {
113 256     256 0 447 my $self = shift;
114              
115 256         358 my ($level, $msg);
116 256 100       814 if ( @_ == 2 ) {
117 251         625 ($level, $msg) = @_;
118             } else {
119 5         22 ($msg) = @_;
120 5         14 $level = 1;
121             }
122              
123 256         829 $msg = "cid=".$self->get_cid.": $msg";
124              
125 256         712 return $self->get_server->log ($level, $msg);
126             }
127              
128             sub input {
129 142     142 0 330 my $self = shift;
130 142         285 my ($e) = @_;
131              
132 142         451 my $server = $self->get_server;
133 142         478 my $message = $self->get_message;
134              
135 142 50       503 if ( not $message ) {
136 142         415 $message = $self->get_message_format->new ($self->get_sock);
137 142         617 $self->set_message($message);
138             }
139              
140 142   100     252 my $request = eval { $message->read } || '';
141 142         1701 my $error = $@;
142              
143 142 50 66     829 return if $request eq '' && $error eq '';
144              
145 142         458 $self->set_message(undef);
146              
147 142 100 66     1277 return $self->disconnect
148             if $request eq "DISCONNECT\n" or
149             $error =~ /DISCONNECTED/;
150              
151 124         570 $server->set_active_connection($self);
152              
153 124         205 my ($cmd, $rc);
154 124 100       441 $cmd = $request->{cmd} if not $error;
155              
156 124         532 $self->log(4, "RPC command: $cmd");
157              
158 124 100 66     1192 if ( $error ) {
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
    100          
    50          
159 3         23 $self->log ("Unexpected error on incoming RPC call: $@");
160 3         92 $rc = {
161             ok => 0,
162             msg => "Unexpected error on incoming RPC call: $@",
163             };
164             }
165             elsif ( $cmd eq 'neg_formats_avail') {
166             $rc = {
167             ok => 1,
168 13         49 msg => join(",", @{$self->get_server->get_message_formats})
  13         66  
169             };
170             }
171             elsif ( $cmd eq 'neg_format_set') {
172 13         269 $rc = $self->client_requests_message_format($request->{msg});
173             }
174             elsif ( $cmd eq 'version' ) {
175             #-- Probably we have fallen back to Storable because an old
176             #-- client has connected. so we change the negotiation
177             #-- message handler to the fallback handler for further
178             #-- communication on this connection.
179 16         97 $self->set_message_format(ref $message);
180              
181 16         711 $rc = {
182             ok => 1,
183             version => $Event::RPC::VERSION,
184             protocol => $Event::RPC::PROTOCOL,
185             };
186             }
187             elsif ( $cmd eq 'auth' ) {
188 2         7 $rc = $self->authorize_user ($request);
189             }
190             elsif ( $server->get_auth_required && !$self->get_is_authenticated ) {
191 0         0 $rc = {
192             ok => 0,
193             msg => "Authorization required",
194             };
195             }
196             elsif ( $cmd eq 'new' ) {
197 8         47 $rc = $self->create_new_object ($request);
198             }
199             elsif ( $cmd eq 'exec' ) {
200 50         171 $rc = $self->execute_object_method ($request);
201             }
202             elsif ( $cmd eq 'classes_list' ) {
203 0         0 $rc = $self->get_classes_list ($request);
204             }
205             elsif ( $cmd eq 'class_info' ) {
206 0         0 $rc = $self->get_class_info ($request);
207             }
208             elsif ( $cmd eq 'class_info_all' ) {
209 15         362 $rc = $self->get_class_info_all ($request);
210             }
211             elsif ( $cmd eq 'client_destroy' ) {
212 4         50 $rc = $self->object_destroyed_on_client ($request);
213             }
214             else {
215 0         0 $self->log ("Unknown request command '$cmd'");
216 0         0 $rc = {
217             ok => 0,
218             msg => "Unknown request command '$cmd'",
219             };
220             }
221              
222 124         678 $server->set_active_connection(undef);
223              
224 124         621 $message->set_data($rc);
225              
226             my $watcher = $self->get_server->get_loop->add_io_watcher (
227             fh => $self->get_sock,
228             poll => 'w',
229             cb => sub {
230 124 50   124   1781 if ( $message->write ) {
231 124 50       473 $self->get_server->get_loop->del_io_watcher($self->get_write_watcher)
232             if $self->get_write_watcher;
233 124         390 $self->set_write_watcher();
234             }
235 124         1644 1;
236             },
237 124         373 );
238              
239 124         464 $self->set_write_watcher($watcher);
240              
241 124         848 1;
242             }
243              
244             sub client_requests_message_format {
245 13     13 0 45 my $self = shift;
246 13         55 my ($client_format) = @_;
247              
248 13         42 foreach my $format ( @{$self->get_server->get_message_formats} ) {
  13         62  
249 13 50       91 if ( $client_format eq $format ) {
250             $self->set_message_format(
251             Event::RPC::Message::Negotiate->known_message_formats
252 13         94 ->{$client_format}
253             );
254              
255 13     12   57 eval "use ".$self->get_message_format;
  12         143  
  12         56  
  12         445  
256 13 50       81 return { ok => 0, msg => "Server rejected format '$client_format': $@" }
257             if $@;
258              
259 13         84 return { ok => 1 };
260             }
261             }
262              
263 0         0 return { ok => 0, msg => "Server rejected format '$client_format'" };
264             }
265              
266             sub authorize_user {
267 2     2 0 3 my $self = shift;
268 2         3 my ($request) = @_;
269              
270 2         4 my $user = $request->{user};
271 2         5 my $pass = $request->{pass};
272              
273 2         5 my $auth_module = $self->get_server->get_auth_module;
274              
275             return {
276 2 50       14 ok => 1,
277             msg => "Not authorization required",
278             } unless $auth_module;
279              
280 2         14 my $ok = $auth_module->check_credentials ($user, $pass);
281              
282 2 100       16 if ( $ok ) {
283 1         5 $self->set_auth_user($user);
284 1         6 $self->set_is_authenticated(1);
285 1         6 $self->log("User '$user' successfully authorized");
286             return {
287 1         16 ok => 1,
288             msg => "Credentials Ok",
289             };
290             }
291             else {
292 1         4 $self->log("Illegal credentials for user '$user'");
293             return {
294 1         5 ok => 0,
295             msg => "Illegal credentials",
296             };
297             }
298             }
299              
300             sub create_new_object {
301 8     8 0 23 my $self = shift;
302 8         38 my ($request) = @_;
303              
304             # Let's create a new object
305 8         33 my $class_method = $request->{method};
306 8         18 my $class = $class_method;
307 8         78 $class =~ s/::[^:]+$//;
308 8         60 $class_method =~ s/^.*:://;
309              
310             # check if access to this class/method is allowed
311 8 50 33     34 if ( not defined $self->get_classes->{$class}->{$class_method} or
312             $self->get_classes->{$class}->{$class_method} ne '_constructor' ) {
313 0         0 $self->log ("Illegal constructor access to $class->$class_method");
314             return {
315 0         0 ok => 0,
316             msg => "Illegal constructor access to $class->$class_method"
317             };
318              
319             }
320              
321             # ok, load class and execute the method
322 8         22 my $object = eval {
323             # load the class if not done yet
324 8 50       30 $self->load_class($class) if $self->get_server->get_load_modules;
325              
326             # resolve object params
327 8         54 $self->resolve_object_params ($request->{params});
328              
329 8         23 $class->$class_method (@{$request->{params}})
  8         95  
330             };
331              
332             # report error
333 8 50       248 if ( $@ ) {
334 0         0 $self->log ("Error: can't create object ".
335             "($class->$class_method): $@");
336             return {
337 0         0 ok => 0,
338             msg => $@,
339             };
340             }
341              
342             # register object
343 8         36 $self->get_server->register_object ($object, $class);
344 8         35 $self->get_client_oids->{"$object"} = 1;
345              
346             # log and return
347 8         61 $self->log (5,
348             "Created new object $class->$class_method with oid '$object'",
349             );
350              
351             return {
352 8         71 ok => 1,
353             oid => "$object",
354             };
355             }
356              
357             sub load_class {
358 0     0 0 0 my $self = shift;
359 0         0 my ($class) = @_;
360              
361 0         0 my $mtime;
362 0         0 my $load_class_info = $self->get_loaded_classes->{$class};
363              
364 0 0 0     0 if ( not $load_class_info or
      0        
365             ( $self->get_server->get_auto_reload_modules &&
366             ( $mtime = (stat($load_class_info->{filename}))[9])
367             > $load_class_info->{mtime} ) )
368             {
369 0 0       0 if ( not $load_class_info->{filename} ) {
370 0         0 my $filename;
371 0         0 my $rel_filename = $class;
372 0         0 $rel_filename =~ s!::!/!g;
373 0         0 $rel_filename .= ".pm";
374              
375 0         0 foreach my $dir ( @INC ) {
376 0 0       0 $filename = "$dir/$rel_filename", last
377             if -f "$dir/$rel_filename";
378             }
379              
380 0 0       0 croak "File for class '$class' not found\n"
381             if not $filename;
382              
383 0         0 $load_class_info->{filename} = $filename;
384 0         0 $load_class_info->{mtime} = 0;
385             }
386              
387 0   0     0 $mtime ||= 0;
388              
389             $self->log (3, "Class '$class' ($load_class_info->{filename}) changed on disk. Reloading...")
390 0 0       0 if $mtime > $load_class_info->{mtime};
391              
392 0         0 do $load_class_info->{filename};
393              
394 0 0       0 if ( $@ ) {
395 0         0 $self->log ("Can't load class '$class': $@");
396 0         0 $load_class_info->{mtime} = 0;
397 0         0 die "Can't load class $class: $@";
398             }
399             else {
400 0         0 $self->log (3, "Class '$class' successfully loaded");
401 0         0 $load_class_info->{mtime} = time;
402             }
403             }
404              
405             $self->log (5, "filename=".$load_class_info->{filename}.
406 0         0 ", mtime=".$load_class_info->{mtime} );
407              
408 0   0     0 $self->get_loaded_classes->{$class} ||= $load_class_info;
409              
410 0         0 1;
411             }
412              
413             sub execute_object_method {
414 50     50 0 72 my $self = shift;
415 50         82 my ($request) = @_;
416              
417             # Method call of an existent object
418 50         87 my $oid = $request->{oid};
419 50         132 my $object_entry = $self->get_objects->{$oid};
420 50         95 my $method = $request->{method};
421              
422 50 50       150 if ( not defined $object_entry ) {
423             # object does not exist
424 0         0 $self->log ("Illegal access to unknown object with oid=$oid");
425             return {
426 0         0 ok => 0,
427             msg => "Illegal access to unknown object with oid=$oid"
428             };
429             }
430              
431 50         92 my $class = $object_entry->{class};
432 50 50 33     118 if ( not defined $self->get_classes->{$class} or
433             not defined $self->get_classes->{$class}->{$method} )
434             {
435             # illegal access to this method
436 0         0 $self->log ("Illegal access to $class->$method");
437             return {
438 0         0 ok => 0,
439             msg => "Illegal access to $class->$method"
440             };
441             }
442              
443 50         119 my $return_type = $self->get_classes->{$class}->{$method};
444              
445             # ok, try loading class and executing the method
446 50         86 my @rc = eval {
447             # (re)load the class if not done yet
448 50 50       102 $self->load_class($class) if $self->get_server->get_load_modules;
449              
450             # resolve object params
451 50         164 $self->resolve_object_params ($request->{params});
452              
453             # exeute method
454 50         101 $object_entry->{object}->$method (@{$request->{params}})
  50         288  
455             };
456              
457             # report error
458 50 50       4586 if ( $@ ) {
459 0         0 $self->log ("Error: can't call '$method' of object ".
460             "with oid=$oid: $@");
461             return {
462 0         0 ok => 0,
463             msg => "$@",
464             };
465             }
466              
467             # log
468 50         214 $self->log (4, "Called method '$method' of object ".
469             "with oid=$oid");
470              
471 50 100       146 if ( $return_type eq '_object' ) {
472             # check if objects are returned by this method
473             # and register them in our internal object table
474             # (if not already done yet)
475 11         25 my $key;
476 11         28 foreach my $rc ( @rc ) {
477 12 100 100     203 if ( ref ($rc) and ref ($rc) !~ /ARRAY|HASH|SCALAR/ ) {
    100          
    100          
478             # returns a single object
479 9         64 $self->log (4, "Method returns object: $rc");
480 9         23 $key = "$rc";
481 9         39 $self->get_client_oids->{$key} = 1;
482 9         30 $self->get_server->register_object($rc, ref $rc);
483 9         26 $rc = $key;
484              
485             }
486             elsif ( ref $rc eq 'ARRAY' ) {
487             # possibly returns a list of objects
488             # make a copy, otherwise the original object references
489             # will be overwritten
490 1         2 my @val = @{$rc};
  1         3  
491 1         3 $rc = \@val;
492 1         3 foreach my $val ( @val ) {
493 10 50 33     56 if ( ref ($val) and ref ($val) !~ /ARRAY|HASH|SCALAR/ ) {
494 10         31 $self->log (4, "Method returns object lref: $val");
495 10         15 $key = "$val";
496 10         21 $self->get_client_oids->{$key} = 1;
497 10         18 $self->get_server->register_object($val, ref $val);
498 10         23 $val = $key;
499             }
500             }
501             }
502             elsif ( ref $rc eq 'HASH' ) {
503             # possibly returns a hash of objects
504             # make a copy, otherwise the original object references
505             # will be overwritten
506 1         4 my %val = %{$rc};
  1         8  
507 1         2 $rc = \%val;
508 1         5 foreach my $val ( values %val ) {
509 10 50 33     58 if ( ref ($val) and ref ($val) !~ /ARRAY|HASH|SCALAR/ ) {
510 10         63 $self->log (4, "Method returns object href: $val");
511 10         17 $key = "$val";
512 10         24 $self->get_client_oids->{$key} = 1;
513 10         19 $self->get_server->register_object($val, ref $val);
514 10         20 $val = $key;
515             }
516             }
517             }
518             }
519             }
520              
521             # return rc
522             return {
523 50         372 ok => 1,
524             rc => \@rc,
525             };
526             }
527              
528             sub object_destroyed_on_client {
529 4     4 0 11 my $self = shift;
530 4         10 my ($request) = @_;
531              
532 4         25 $self->log(5, "Object with oid=$request->{oid} destroyed on client");
533              
534 4         35 delete $self->get_client_oids->{$request->{oid}};
535 4         17 $self->get_server->deregister_object($request->{oid});
536              
537             return {
538 4         15 ok => 1
539             };
540             }
541              
542             sub get_classes_list {
543 0     0 0 0 my $self = shift;
544 0         0 my ($request) = @_;
545              
546 0         0 my @classes = keys %{$self->get_classes};
  0         0  
547              
548             return {
549 0         0 ok => 1,
550             classes => \@classes,
551             }
552             }
553              
554             sub get_class_info {
555 0     0 0 0 my $self = shift;
556 0         0 my ($request) = @_;
557              
558 0         0 my $class = $request->{class};
559              
560 0 0       0 if ( not defined $self->get_classes->{$class} ) {
561 0         0 $self->log ("Unknown class '$class'");
562             return {
563 0         0 ok => 0,
564             msg => "Unknown class '$class'"
565             };
566             }
567              
568 0         0 $self->log (4, "Class info for '$class' requested");
569              
570             return {
571             ok => 1,
572 0         0 methods => $self->get_classes->{$class},
573             };
574             }
575              
576             sub get_class_info_all {
577 15     15 0 61 my $self = shift;
578 15         48 my ($request) = @_;
579              
580             return {
581 15         127 ok => 1,
582             class_info_all => $self->get_classes,
583             }
584             }
585              
586             sub resolve_object_params {
587 58     58 0 87 my $self = shift;
588 58         84 my ($params) = @_;
589              
590 58         84 my $key;
591 58         77 foreach my $par ( @{$params} ) {
  58         161  
592 33 50       80 if ( defined $self->get_classes->{ref($par)} ) {
593 0         0 $key = ${$par};
  0         0  
594 0         0 $key = "$key";
595             croak "unknown object with key '$key'"
596 0 0       0 if not defined $self->get_objects->{$key};
597 0         0 $par = $self->get_objects->{$key}->{object};
598             }
599             }
600              
601 58         121 1;
602             }
603              
604             1;
605              
606             __END__