File Coverage

blib/lib/Event/RPC/Connection.pm
Criterion Covered Total %
statement 6 249 2.4
branch 0 92 0.0
condition 0 37 0.0
subroutine 2 35 5.7
pod 0 31 0.0
total 8 444 1.8


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