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 25     25   193 use strict;
  25         73  
  25         810  
4 22     22   139 use utf8;
  22         59  
  22         393  
5              
6 22     22   490 use Carp;
  22         54  
  22         2312  
7              
8 22     22   4511 use Event::RPC::Message::Negotiate;
  22         58  
  22         73335  
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 300     300 0 1055 sub get_cid { shift->{cid} }
17 344     344 0 3143 sub get_sock { shift->{sock} }
18 1006     1006 0 3069 sub get_server { shift->{server} }
19              
20 225     225 0 1082 sub get_classes { shift->{server}->{classes} }
21 0     0 0 0 sub get_loaded_classes { shift->{server}->{loaded_classes} }
22 50     50 0 115 sub get_objects { shift->{server}->{objects} }
23 67     67 0 379 sub get_client_oids { shift->{client_oids} }
24              
25 190     190 0 2146 sub get_message_format { shift->{message_format} }
26 23     23 0 106 sub get_watcher { shift->{watcher} }
27 321     321 0 1048 sub get_write_watcher { shift->{write_watcher} }
28 172     172 0 309 sub get_message { shift->{message} }
29 7     7 0 69 sub get_is_authenticated { shift->{is_authenticated} }
30 0     0 0 0 sub get_auth_user { shift->{auth_user} }
31              
32 39     39 0 115 sub set_message_format { shift->{message_format} = $_[1] }
33 23     23 0 74 sub set_watcher { shift->{watcher} = $_[1] }
34 321     321 0 580 sub set_write_watcher { shift->{write_watcher} = $_[1] }
35 344     344 0 665 sub set_message { shift->{message} = $_[1] }
36 4     4 0 9 sub set_is_authenticated { shift->{is_authenticated} = $_[1] }
37 4     4 0 10 sub set_auth_user { shift->{auth_user} = $_[1] }
38              
39             sub new {
40 23     23 0 99 my $class = shift;
41 23         88 my ($server, $sock) = @_;
42              
43 23         85 my $cid = ++$CONNECTION_ID;
44              
45 23         165 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 23 50       142 if ( $sock ) {
59 23         285 $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 172     172   176819 cb => sub { $self->input; 1 },
  172         1116  
66 23         210 desc => "rpc client cid=$cid",
67             );
68             }
69              
70 23         121 my $connection_hook = $server->get_connection_hook;
71 23 50       225 &$connection_hook($self, "connect") if $connection_hook;
72              
73 23         460 return $self;
74             }
75              
76             sub disconnect {
77 23     23 0 144 my $self = shift;
78              
79 23         90 $self->get_server->get_loop->del_io_watcher($self->get_watcher);
80 23 50       119 $self->get_server->get_loop->del_io_watcher($self->get_write_watcher)
81             if $self->get_write_watcher;
82 23         124 $self->set_watcher(undef);
83 23         76 $self->set_write_watcher(undef);
84 23         65 close $self->get_sock;
85              
86 23         1590 my $server = $self->get_server;
87              
88 23         80 $server->set_clients_connected ( $self->get_server->get_clients_connected - 1 );
89              
90 23         42 foreach my $oid ( keys %{$self->get_client_oids} ) {
  23         98  
91 25         97 $server->deregister_object($oid);
92             }
93              
94 23         131 $self->log(2, "Client disconnected");
95              
96 23         82 my $connection_hook = $server->get_connection_hook;
97 23 50       155 &$connection_hook($self, "disconnect") if $connection_hook;
98              
99 23         135 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 298     298 0 458 my $self = shift;
114              
115 298         470 my ($level, $msg);
116 298 100       752 if ( @_ == 2 ) {
117 288         587 ($level, $msg) = @_;
118             } else {
119 10         30 ($msg) = @_;
120 10         27 $level = 1;
121             }
122              
123 298         696 $msg = "cid=".$self->get_cid.": $msg";
124              
125 298         781 return $self->get_server->log ($level, $msg);
126             }
127              
128             sub input {
129 172     172 0 327 my $self = shift;
130 172         378 my ($e) = @_;
131              
132 172         379 my $server = $self->get_server;
133 172         443 my $message = $self->get_message;
134              
135 172 50       473 if ( not $message ) {
136 172         381 $message = $self->get_message_format->new ($self->get_sock);
137 172         440 $self->set_message($message);
138             }
139              
140 172   100     275 my $request = eval { $message->read } || '';
141 172         1139 my $error = $@;
142              
143 172 50 66     671 return if $request eq '' && $error eq '';
144              
145 172         631 $self->set_message(undef);
146              
147 172 100 66     954 return $self->disconnect
148             if $request eq "DISCONNECT\n" or
149             $error =~ /DISCONNECTED/;
150              
151 149         599 $server->set_active_connection($self);
152              
153 149         229 my ($cmd, $rc);
154 149 100       446 $cmd = $request->{cmd} if not $error;
155              
156 149         568 $self->log(4, "RPC command: $cmd");
157              
158 149 100 66     839 if ( $error ) {
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    50          
    100          
    50          
159 3         27 $self->log ("Unexpected error on incoming RPC call: $@");
160 3         46 $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 18         56 msg => join(",", @{$self->get_server->get_message_formats})
  18         63  
169             };
170             }
171             elsif ( $cmd eq 'neg_format_set') {
172 18         110 $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 21         99 $self->set_message_format(ref $message);
180              
181 21         296 $rc = {
182             ok => 1,
183             version => $Event::RPC::VERSION,
184             protocol => $Event::RPC::PROTOCOL,
185             };
186             }
187             elsif ( $cmd eq 'auth' ) {
188 7         28 $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 10         131 $rc = $self->create_new_object ($request);
198             }
199             elsif ( $cmd eq 'exec' ) {
200 50         186 $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 18         227 $rc = $self->get_class_info_all ($request);
210             }
211             elsif ( $cmd eq 'client_destroy' ) {
212 4         46 $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 149         585 $server->set_active_connection(undef);
223              
224 149         612 $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 149 50   149   3235 if ( $message->write ) {
231 149 50       446 $self->get_server->get_loop->del_io_watcher($self->get_write_watcher)
232             if $self->get_write_watcher;
233 149         339 $self->set_write_watcher();
234             }
235 149         1525 1;
236             },
237 149         328 );
238              
239 149         444 $self->set_write_watcher($watcher);
240              
241 149         698 1;
242             }
243              
244             sub client_requests_message_format {
245 18     18 0 45 my $self = shift;
246 18         69 my ($client_format) = @_;
247              
248 18         55 foreach my $format ( @{$self->get_server->get_message_formats} ) {
  18         64  
249 18 50       91 if ( $client_format eq $format ) {
250             $self->set_message_format(
251             Event::RPC::Message::Negotiate->known_message_formats
252 18         92 ->{$client_format}
253             );
254              
255 18     15   69 eval "use ".$self->get_message_format;
  15         121  
  15         34  
  15         305  
256 18 50       93 return { ok => 0, msg => "Server rejected format '$client_format': $@" }
257             if $@;
258              
259 18         86 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 7     7 0 15 my $self = shift;
268 7         16 my ($request) = @_;
269              
270 7         15 my $user = $request->{user};
271 7         18 my $pass = $request->{pass};
272              
273 7         19 my $auth_module = $self->get_server->get_auth_module;
274              
275             return {
276 7 50       66 ok => 1,
277             msg => "Not authorization required",
278             } unless $auth_module;
279              
280 7         39 my $ok = $auth_module->check_credentials ($user, $pass);
281              
282 7 100       19 if ( $ok ) {
283 4         18 $self->set_auth_user($user);
284 4         14 $self->set_is_authenticated(1);
285 4         18 $self->log("User '$user' successfully authorized");
286             return {
287 4         16 ok => 1,
288             msg => "Credentials Ok",
289             };
290             }
291             else {
292 3         25 $self->log("Illegal credentials for user '$user'");
293             return {
294 3         14 ok => 0,
295             msg => "Illegal credentials",
296             };
297             }
298             }
299              
300             sub create_new_object {
301 10     10 0 31 my $self = shift;
302 10         49 my ($request) = @_;
303              
304             # Let's create a new object
305 10         30 my $class_method = $request->{method};
306 10         25 my $class = $class_method;
307 10         91 $class =~ s/::[^:]+$//;
308 10         58 $class_method =~ s/^.*:://;
309              
310             # check if access to this class/method is allowed
311 10 50 33     51 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 10         30 my $object = eval {
323             # load the class if not done yet
324 10 50       41 $self->load_class($class) if $self->get_server->get_load_modules;
325              
326             # resolve object params
327 10         64 $self->resolve_object_params ($request->{params});
328              
329 10         19 $class->$class_method (@{$request->{params}})
  10         130  
330             };
331              
332             # report error
333 10 50       339 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 10         41 $self->get_server->register_object ($object, $class);
344 10         42 $self->get_client_oids->{"$object"} = 1;
345              
346             # log and return
347 10         68 $self->log (5,
348             "Created new object $class->$class_method with oid '$object'",
349             );
350              
351             return {
352 10         82 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 87 my $self = shift;
415 50         97 my ($request) = @_;
416              
417             # Method call of an existent object
418 50         95 my $oid = $request->{oid};
419 50         117 my $object_entry = $self->get_objects->{$oid};
420 50         111 my $method = $request->{method};
421              
422 50 50       116 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         104 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         127 my $return_type = $self->get_classes->{$class}->{$method};
444              
445             # ok, try loading class and executing the method
446 50         88 my @rc = eval {
447             # (re)load the class if not done yet
448 50 50       141 $self->load_class($class) if $self->get_server->get_load_modules;
449              
450             # resolve object params
451 50         160 $self->resolve_object_params ($request->{params});
452              
453             # exeute method
454 50         90 $object_entry->{object}->$method (@{$request->{params}})
  50         260  
455             };
456              
457             # report error
458 50 50       2492 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         220 $self->log (4, "Called method '$method' of object ".
469             "with oid=$oid");
470              
471 50 100       127 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         21 my $key;
476 11         23 foreach my $rc ( @rc ) {
477 12 100 100     148 if ( ref ($rc) and ref ($rc) !~ /ARRAY|HASH|SCALAR/ ) {
    100          
    100          
478             # returns a single object
479 9         69 $self->log (4, "Method returns object: $rc");
480 9         21 $key = "$rc";
481 9         30 $self->get_client_oids->{$key} = 1;
482 9         6151 $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         4 my @val = @{$rc};
  1         4  
491 1         5 $rc = \@val;
492 1         5 foreach my $val ( @val ) {
493 10 50 33     84 if ( ref ($val) and ref ($val) !~ /ARRAY|HASH|SCALAR/ ) {
494 10         51 $self->log (4, "Method returns object lref: $val");
495 10         25 $key = "$val";
496 10         28 $self->get_client_oids->{$key} = 1;
497 10         26 $self->get_server->register_object($val, ref $val);
498 10         28 $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         11 my %val = %{$rc};
  1         13  
507 1         7 $rc = \%val;
508 1         6 foreach my $val ( values %val ) {
509 10 50 33     74 if ( ref ($val) and ref ($val) !~ /ARRAY|HASH|SCALAR/ ) {
510 10         53 $self->log (4, "Method returns object href: $val");
511 10         27 $key = "$val";
512 10         27 $self->get_client_oids->{$key} = 1;
513 10         24 $self->get_server->register_object($val, ref $val);
514 10         30 $val = $key;
515             }
516             }
517             }
518             }
519             }
520              
521             # return rc
522             return {
523 50         287 ok => 1,
524             rc => \@rc,
525             };
526             }
527              
528             sub object_destroyed_on_client {
529 4     4 0 14 my $self = shift;
530 4         13 my ($request) = @_;
531              
532 4         24 $self->log(5, "Object with oid=$request->{oid} destroyed on client");
533              
534 4         13 delete $self->get_client_oids->{$request->{oid}};
535 4         13 $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 18     18 0 102 my $self = shift;
578 18         65 my ($request) = @_;
579              
580             return {
581 18         113 ok => 1,
582             class_info_all => $self->get_classes,
583             }
584             }
585              
586             sub resolve_object_params {
587 60     60 0 102 my $self = shift;
588 60         115 my ($params) = @_;
589              
590 60         86 my $key;
591 60         81 foreach my $par ( @{$params} ) {
  60         168  
592 37 50       124 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 60         116 1;
602             }
603              
604             1;
605              
606             __END__