File Coverage

blib/lib/Event/RPC/Client.pm
Criterion Covered Total %
statement 251 297 84.5
branch 72 102 70.5
condition 12 25 48.0
subroutine 54 71 76.0
pod 8 55 14.5
total 397 550 72.1


line stmt bran cond sub pod time code
1             #-----------------------------------------------------------------------
2             # Copyright (C) 2005-2015 by Jörn Reder .
3             # All Rights Reserved. See file COPYRIGHT for details.
4             #
5             # This module is part of Event::RPC, which is free software; you can
6             # redistribute it and/or modify it under the same terms as Perl itself.
7             #-----------------------------------------------------------------------
8              
9             package Event::RPC::Client;
10              
11 67     67   6930774 use Event::RPC;
  67         391  
  67         2438  
12 31     31   9155 use Event::RPC::Message::Negotiate;
  31         125  
  31         880  
13              
14 31     31   173 use Carp;
  31         78  
  31         1622  
15 31     31   142 use strict;
  31         56  
  31         805  
16 31     31   125 use utf8;
  31         77  
  31         147  
17              
18 31     31   6095 use IO::Socket;
  31         203899  
  31         223  
19              
20             #-- This can be changed for testing purposes e.g. to simulate
21             #-- old clients connecting straight with Storable format.
22             $Event::RPC::Client::DEFAULT_MESSAGE_FORMAT = "Event::RPC::Message::Negotiate";
23              
24 79     79 1 605 sub get_client_version { $Event::RPC::VERSION }
25 79     79 1 771 sub get_client_protocol { $Event::RPC::PROTOCOL }
26              
27 0     0 0 0 sub get_host { shift->{host} }
28 91     91 0 435 sub get_port { shift->{port} }
29 989     989 0 54344 sub get_sock { shift->{sock} }
30 91     91 0 296 sub get_timeout { shift->{timeout} }
31 76     76 0 313 sub get_classes { shift->{classes} }
32 152     152 0 330 sub get_class_map { shift->{class_map} }
33 152     152 0 420 sub get_loaded_classes { shift->{loaded_classes} }
34 7     7 0 56 sub get_error_cb { shift->{error_cb} }
35 91     91 0 231 sub get_ssl { shift->{ssl} }
36 12     12 0 75 sub get_ssl_ca_file { shift->{ssl_ca_file} }
37 7     7 0 46 sub get_ssl_ca_path { shift->{ssl_ca_path} }
38 7     7 0 48 sub get_ssl_opts { shift->{ssl_opts} }
39 79     79 0 215 sub get_auth_user { shift->{auth_user} }
40 79     79 0 205 sub get_auth_pass { shift->{auth_pass} }
41 301     301 0 108388 sub get_connected { shift->{connected} }
42 91     91 0 415 sub get_server { shift->{server} }
43 0     0 1 0 sub get_server_version { shift->{server_version} }
44 0     0 1 0 sub get_server_protocol { shift->{server_protocol} }
45 962     962 0 33583 sub get_message_format { shift->{message_format} }
46 76     76 0 1872 sub get_insecure_msg_fmt_ok { shift->{insecure_msg_fmt_ok} }
47              
48 0     0 0 0 sub set_host { shift->{host} = $_[1] }
49 0     0 0 0 sub set_port { shift->{port} = $_[1] }
50 89     89 0 1144 sub set_sock { shift->{sock} = $_[1] }
51 0     0 0 0 sub set_timeout { shift->{timeout} = $_[1] }
52 0     0 0 0 sub set_classes { shift->{classes} = $_[1] }
53 0     0 0 0 sub set_class_map { shift->{class_map} = $_[1] }
54 0     0 0 0 sub set_loaded_classes { shift->{loaded_classes} = $_[1] }
55 0     0 0 0 sub set_error_cb { shift->{error_cb} = $_[1] }
56 0     0 0 0 sub set_ssl { shift->{ssl} = $_[1] }
57 1     1 0 19292 sub set_ssl_ca_file { shift->{ssl_ca_file} = $_[1] }
58 0     0 0 0 sub set_ssl_ca_path { shift->{ssl_ca_path} = $_[1] }
59 0     0 0 0 sub set_ssl_opts { shift->{ssl_opts} = $_[1] }
60 0     0 0 0 sub set_auth_user { shift->{auth_user} = $_[1] }
61 3     3 0 16 sub set_auth_pass { shift->{auth_pass} = $_[1] }
62 229     229 0 738 sub set_connected { shift->{connected} = $_[1] }
63 0     0 0 0 sub set_server { shift->{server} = $_[1] }
64 79     79 0 992 sub set_server_version { shift->{server_version} = $_[1] }
65 79     79 0 977 sub set_server_protocol { shift->{server_protocol} = $_[1] }
66 153     153 0 583 sub set_message_format { shift->{message_format} = $_[1] }
67 0     0 0 0 sub set_insecure_msg_fmt_ok { shift->{insecure_msg_fmt_ok} = $_[1] }
68              
69             sub get_max_packet_size {
70 1     1 1 6 return Event::RPC::Message->get_max_packet_size;
71             }
72              
73             sub set_max_packet_size {
74 1     1 1 1955 my $class = shift;
75 1         4 my ($value) = @_;
76 1         6 Event::RPC::Message->set_max_packet_size($value);
77             }
78              
79             sub new {
80 87     87 0 81894 my $class = shift;
81 87         3498 my %par = @_;
82             my ($server, $host, $port, $classes, $class_map, $error_cb, $timeout) =
83 87         1129 @par{'server','host','port','classes','class_map','error_cb','timeout'};
84             my ($ssl, $ssl_ca_file, $ssl_opts, $auth_user, $auth_pass, $insecure_msg_fmt_ok) =
85 87         67833 @par{'ssl','ssl_ca_file','ssl_opts','auth_user','auth_pass','insecure_msg_fmt_ok'};
86              
87 87   50     3596 $server ||= '';
88 87   50     440 $host ||= '';
89 87 100       579 $insecure_msg_fmt_ok = 1 unless defined $insecure_msg_fmt_ok;
90              
91 87 50 33     753 if ( $server ne '' and $host eq '' ) {
92 0         0 warn "Option 'server' is deprecated. Use 'host' instead.";
93 0         0 $host = $server;
94             }
95              
96 87         9674 my $self = bless {
97             host => $server,
98             server => $host,
99             port => $port,
100             timeout => $timeout,
101             classes => $classes,
102             class_map => $class_map,
103             ssl => $ssl,
104             ssl_ca_file => $ssl_ca_file,
105             ssl_opts => $ssl_opts,
106             auth_user => $auth_user,
107             auth_pass => $auth_pass,
108             error_cb => $error_cb,
109             message_format => $Event::RPC::Client::DEFAULT_MESSAGE_FORMAT,
110             insecure_msg_fmt_ok => $insecure_msg_fmt_ok,
111             loaded_classes => {},
112             connected => 0,
113             }, $class;
114              
115 87         816 return $self;
116             }
117              
118             sub connect {
119 91     91 1 783 my $self = shift;
120              
121 91 50       3945 croak "Client is already connected" if $self->get_connected;
122              
123 91         517 my $ssl = $self->get_ssl;
124 91         388 my $server = $self->get_server;
125 91         566 my $port = $self->get_port;
126 91         442 my $timeout = $self->get_timeout;
127              
128 91         1744 $self->set_message_format($Event::RPC::Client::DEFAULT_MESSAGE_FORMAT);
129              
130             #-- Client may try to fallback to Storable
131 91 100 100     345 Event::RPC::Message::Negotiate->set_storable_fallback_ok(1)
132             if $self->get_message_format eq 'Event::RPC::Message::Negotiate' and
133             $self->get_insecure_msg_fmt_ok;
134              
135 91 100       367 if ( $ssl ) {
136 7         18 eval { require IO::Socket::SSL };
  7         107  
137 7 50       47 croak "SSL requested, but IO::Socket::SSL not installed" if $@;
138             }
139              
140 91         204 my $sock;
141 91 100       642 if ( $ssl ) {
    50          
142 7         15 my @verify_opts;
143 7 100 66     30 if ( $self->get_ssl_ca_file or $self->get_ssl_ca_path ) {
144 5         51 push @verify_opts, (
145             SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER(),
146             SSL_ca_file => $self->get_ssl_ca_file,
147             SSL_ca_path => $self->get_ssl_ca_path,
148             );
149             }
150             else {
151 2         8 push @verify_opts, (
152             SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),
153             );
154             }
155              
156 7         53 my $ssl_opts = $self->get_ssl_opts;
157              
158             $sock = IO::Socket::SSL->new(
159             Proto => 'tcp',
160             PeerPort => $port,
161             PeerAddr => $server,
162             Type => SOCK_STREAM,
163             Timeout => $timeout,
164             @verify_opts,
165 7 50       269 ($ssl_opts?%{$ssl_opts}:()),
  0 100       0  
166             )
167             or croak "Can't open SSL connection to $server:$port: $IO::Socket::SSL::ERROR";
168             }
169             elsif ($server eq "unix/") {
170 0 0       0 $sock = IO::Socket::UNIX->new(
171             Type => IO::Socket::UNIX::SOCK_STREAM(),
172             Peer => $port,
173             )
174             or croak "Can't open Unix Domain connection to $server:$port - $!";
175             }
176             else {
177 84 50       942 $sock = IO::Socket::INET->new(
178             Proto => 'tcp',
179             PeerPort => $port,
180             PeerAddr => $server,
181             Type => SOCK_STREAM,
182             Timeout => $timeout,
183             )
184             or croak "Can't open TCP connection to $server:$port - $!";
185             }
186              
187 89         376522 $sock->autoflush(1);
188              
189 89         6876 $self->set_sock($sock);
190              
191 89         3663 eval {
192             #-- Perform message format negotitation if we are not
193             #-- configured to a specific format already.
194 89 100       329 $self->negotiate_message_format
195             if $self->get_message_format eq 'Event::RPC::Message::Negotiate';
196              
197 86         2007 $self->check_version;
198             };
199              
200 89 100       358 if ( $@ ) {
201 10         115 $self->disconnect;
202 10         77 die $@;
203             }
204              
205 79         339 my $auth_user = $self->get_auth_user;
206 79         226 my $auth_pass = $self->get_auth_pass;
207              
208 79 100       224 if ( $auth_user ) {
209 7         128 my $rc = $self->send_request(
210             { cmd => 'auth',
211             user => $auth_user,
212             pass => $auth_pass,
213             }
214             );
215 4 50       33 if ( not $rc->{ok} ) {
216 0         0 $self->disconnect;
217 0         0 croak $rc->{msg};
218             }
219             }
220              
221 76 50       393 if ( not $self->get_classes ) {
222 76         304 $self->load_all_classes;
223             }
224             else {
225 0         0 $self->load_classes;
226             }
227              
228 76         289 $self->set_connected(1);
229              
230 76         436 1;
231             }
232              
233             sub log_connect {
234 174     174 0 63409083 my $class = shift;
235 174         9073 my %par = @_;
236 174         3365 my ( $server, $port ) = @par{ 'server', 'port' };
237              
238 174         731 my $sock;
239 174 50       2395 if ($server eq "unix/") {
240 0 0       0 $sock = IO::Socket::UNIX->new(
241             Type => IO::Socket::UNIX::SOCK_STREAM(),
242             Peer => $port,
243             )
244             or croak "Can't open Unix Domain log connection to $server:$port - $!";
245             }
246             else {
247 174 100       12253 $sock = IO::Socket::INET->new(
248             Proto => 'tcp',
249             PeerPort => $port,
250             PeerAddr => $server,
251             Type => SOCK_STREAM,
252             )
253             or croak "Can't open TCP log connection to $server:$port - $!";
254             }
255              
256 87         67760 return $sock;
257             }
258              
259             sub disconnect {
260 153     153 1 17045 my $self = shift;
261              
262 153 100       697 close( $self->get_sock ) if $self->get_sock;
263 153         2587 $self->set_connected(0);
264              
265 153         2693 1;
266             }
267              
268             sub DESTROY {
269 67     67   14074430 shift->disconnect;
270             }
271              
272             sub error {
273 7     7 0 56 my $self = shift;
274 7         35 my ($message) = @_;
275              
276 7         70 my $error_cb = $self->get_error_cb;
277              
278 7 50       56 if ($error_cb) {
279 0         0 &$error_cb( $self, $message );
280             }
281             else {
282 7         98 die "Unhandled error in client/server communication: $message";
283             }
284              
285 0         0 1;
286             }
287              
288             sub negotiate_message_format {
289 65     65 0 172 my $self = shift;
290              
291 65         167 my $rc = eval {
292 65         1369 $self->send_request({
293             cmd => "neg_formats_avail"
294             })
295             };
296              
297 65 100       479 if ( $@ ) {
298             #-- On error we probably may fall back to Storable
299             #-- (we connected to an old server)
300 9 100       72 if ( $self->get_insecure_msg_fmt_ok ) {
301 6         48 require Event::RPC::Message::Storable;
302 6         42 $self->set_message_format("Event::RPC::Message::Storable");
303 6         12 return;
304             }
305              
306             #-- die if Storable is not allowed
307 3         45 die "Error on message format negotiation and client is not ".
308             "allowed to fall back to Storable\n";
309             }
310              
311 56         299 my $modules_by_format_name =
312             Event::RPC::Message::Negotiate->known_message_formats;
313              
314 56         543 my @formats = split(/,/, $rc->{msg});
315              
316 56         421 my $format_chosen = '';
317 56         288 my $module_chosen = '';
318 56         430 foreach my $format ( @formats ) {
319 56 50       306 my $module = $modules_by_format_name->{$format}
320             or die "Unknown message format '$format";
321              
322 56     20   12669 eval "use $module";
  20         8217  
  20         82  
  20         628  
323              
324 56 50       276 if ( not $@ ) {
325 56         139 $format_chosen = $format;
326 56         123 $module_chosen = $module;
327 56         199 last;
328             };
329             }
330              
331 56 50       174 die "Can't negotiate message format\n" unless $format_chosen;
332              
333 56         119 eval {
334 56         423 $self->send_request({
335             cmd => "neg_format_set",
336             msg => $format_chosen,
337             })
338             };
339              
340 56 50       252 die "Error on neg_format_set: $@" if $@;
341              
342 56         588 $self->set_message_format($module_chosen);
343              
344 56         673 1;
345             }
346              
347             sub check_version {
348 86     86 0 226 my $self = shift;
349              
350 86         235 my $rc = eval { $self->send_request( { cmd => 'version', } ) };
  86         700  
351 86 100       960 die "CATCHED $@" if $@;
352              
353 79         442 $self->set_server_version( $rc->{version} );
354 79         485 $self->set_server_protocol( $rc->{protocol} );
355              
356 79 50       664 if ( $rc->{version} ne $self->get_client_version ) {
357 0         0 warn "Event::RPC warning: server version $rc->{version} != "
358             . "client version "
359             . $self->get_client_version;
360             }
361              
362 79 50       386 if ( $rc->{protocol} < $self->get_client_protocol ) {
363 0         0 die "FATAL: Server protocol version $rc->{protocol} < "
364             . "client protocol version "
365             . $self->get_client_protocol;
366             }
367              
368 79         454 1;
369             }
370              
371             sub load_all_classes {
372 76     76 0 171 my $self = shift;
373              
374 76         408 my $rc = $self->send_request( { cmd => 'class_info_all', } );
375              
376 76         271 my $class_info_all = $rc->{class_info_all};
377              
378 76         154 foreach my $class ( keys %{$class_info_all} ) {
  76         1401  
379 152         870 $self->load_class( $class, $class_info_all->{$class} );
380             }
381              
382 76         616 1;
383             }
384              
385             sub load_classes {
386 0     0 0 0 my $self = shift;
387              
388 0         0 my $classes = $self->get_classes;
389 0         0 my %classes;
390 0         0 @classes{ @{$classes} } = (1) x @{$classes};
  0         0  
  0         0  
391              
392 0         0 my $rc = $self->send_request( { cmd => 'classes_list', } );
393              
394 0         0 foreach my $class ( @{ $rc->{classes} } ) {
  0         0  
395 0 0       0 next if not $classes{$class};
396 0         0 $classes{$class} = 0;
397              
398 0         0 my $rc = $self->send_request(
399             { cmd => 'class_info',
400             class => $class,
401             }
402             );
403              
404 0         0 $self->load_class( $class, $rc->{methods} );
405             }
406              
407 0         0 foreach my $class ( @{$classes} ) {
  0         0  
408             warn "WARNING: Class '$class' not exported by server"
409 0 0       0 if $classes{$class};
410             }
411              
412 0         0 1;
413             }
414              
415             sub load_class {
416 152     152 0 325 my $self = shift;
417 152         379 my ( $class, $methods ) = @_;
418              
419 152         693 my $loaded_classes = $self->get_loaded_classes;
420 152 50       519 return 1 if $loaded_classes->{$class};
421 152         356 $loaded_classes->{$class} = 1;
422              
423 152         261 my $local_method;
424 152         398 my $class_map = $self->get_class_map;
425 152   33     894 my $local_class = $class_map->{$class} || $class;
426              
427             # create local destructor for this class
428             {
429 31     31   96284 no strict 'refs';
  31         55  
  31         5171  
  152         261  
430 152         507 my $local_method = $local_class . '::' . "DESTROY";
431             *$local_method = sub {
432 165 100   165   64046 return if not $self->get_connected;
433 34         48 my $oid_ref = shift;
434             $self->send_request({
435             cmd => "client_destroy",
436 34         83 oid => ${$oid_ref},
  34         779  
437             });
438 152         4213 };
439             }
440              
441             # create local methods for this class
442 152         334 foreach my $method ( keys %{$methods} ) {
  152         1412  
443 1520         2576 $local_method = $local_class . '::' . $method;
444              
445 1520         2726 my $method_type = $methods->{$method};
446              
447 1520 100       3276 if ( $method_type eq '_constructor' ) {
    100          
448             # this is a constructor for this class
449 228         448 my $request_method = $class . '::' . $method;
450 31     31   190 no strict 'refs';
  31         47  
  31         4376  
451             *$local_method = sub {
452 40     40   5607 shift;
453 40         1157 my $rc = $self->send_request({
454             cmd => 'new',
455             method => $request_method,
456             params => \@_,
457             });
458 40         216 my $oid = $rc->{oid};
459 40         324 return bless \$oid, $local_class;
460 228         2279 };
461             }
462             elsif ( $method_type eq '1' ) {
463             # this is a simple method
464 836         1274 my $request_method = $method;
465 31     31   184 no strict 'refs';
  31         62  
  31         4230  
466             *$local_method = sub {
467 249     249   32063 my $oid_ref = shift;
468             my $rc = $self->send_request({
469             cmd => 'exec',
470 249         455 oid => ${$oid_ref},
  249         2298  
471             method => $request_method,
472             params => \@_,
473             });
474 248 50       1005 return unless $rc;
475 248         1336 $rc = $rc->{rc};
476 248 100       555 return @{$rc} if wantarray;
  1         8  
477 247         3600 return $rc->[0];
478 836         7167 };
479             }
480             else {
481             # this is a object returner
482 456         783 my $request_method = $method;
483 31     31   226 no strict 'refs';
  31         58  
  31         20799  
484             *$local_method = sub {
485 71     71   675 my $oid_ref = shift;
486             my $rc = $self->send_request({
487             cmd => 'exec',
488 71         110 oid => ${$oid_ref},
  71         756  
489             method => $request_method,
490             params => \@_,
491             });
492 71 50       330 return unless $rc;
493 71         477 $rc = $rc->{rc};
494              
495 71         122 foreach my $val ( @{$rc} ) {
  71         218  
496 72 100       411 if ( ref $val eq 'ARRAY' ) {
    100          
    100          
497 1         4 foreach my $list_elem ( @{$val} ) {
  1         3  
498 10         27 my ($class) = split( "=", "$list_elem", 2 );
499             $self->load_class($class)
500 10 50       28 unless $loaded_classes->{$class};
501 10         18 my $list_elem_copy = $list_elem;
502 10         18 $list_elem = \$list_elem_copy;
503             bless $list_elem,
504 10   33     38 ( $class_map->{$class} || $class );
505             }
506             }
507             elsif ( ref $val eq 'HASH' ) {
508 1         29 foreach my $hash_elem ( values %{$val} ) {
  1         8  
509 10         23 my ($class) = split( "=", "$hash_elem", 2 );
510             $self->load_class($class)
511 10 50       26 unless $loaded_classes->{$class};
512 10         18 my $hash_elem_copy = $hash_elem;
513 10         75 $hash_elem = \$hash_elem_copy;
514             bless $hash_elem,
515 10   33     56 ( $class_map->{$class} || $class );
516             }
517             }
518             elsif ( defined $val ) {
519 69         416 my ($class) = split( "=", "$val", 2 );
520             $self->load_class($class)
521 69 50       293 unless $loaded_classes->{$class};
522 69         122 my $val_copy = $val;
523 69         118 $val = \$val_copy;
524 69   33     1110 bless $val, ( $class_map->{$class} || $class );
525             }
526             }
527 71 100       232 return @{$rc} if wantarray;
  1         26  
528 70         324 return $rc->[0];
529 456         5577 };
530             }
531             }
532              
533 152         641 return $local_class;
534             }
535              
536             sub send_request {
537 684     684 0 1225 my $self = shift;
538 684         1684 my ($request) = @_;
539              
540 684         1933 my $message = $self->get_message_format->new( $self->get_sock );
541              
542 684         4011 $message->write_blocked($request);
543              
544 684         1206 my $rc = eval { $message->read_blocked };
  684         3413  
545              
546 684 100       4419 if ($@) {
547 7         126 $self->error($@);
548 0         0 return;
549             }
550              
551 677 100       1608 if ( not $rc->{ok} ) {
552 13 100       118 $rc->{msg} .= "\n" if not $rc->{msg} =~ /\n$/;
553 13         3588 croak ("$rc->{msg} -- called via Event::RPC::Client");
554             }
555              
556 664         4116 return $rc;
557             }
558              
559             1;
560              
561             __END__