File Coverage

blib/lib/Event/RPC/Client.pm
Criterion Covered Total %
statement 249 293 84.9
branch 70 94 74.4
condition 12 25 48.0
subroutine 54 71 76.0
pod 8 55 14.5
total 393 538 73.0


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   741968 use Event::RPC;
  67         259  
  67         1990  
12 31     31   5677 use Event::RPC::Message::Negotiate;
  31         45  
  31         605  
13              
14 31     31   155 use Carp;
  31         48  
  31         1264  
15 31     31   119 use strict;
  31         57  
  31         576  
16 31     31   110 use utf8;
  31         53  
  31         132  
17              
18 31     31   4536 use IO::Socket::INET;
  31         148237  
  31         277  
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 612 sub get_client_version { $Event::RPC::VERSION }
25 79     79 1 795 sub get_client_protocol { $Event::RPC::PROTOCOL }
26              
27 0     0 0 0 sub get_host { shift->{host} }
28 91     91 0 542 sub get_port { shift->{port} }
29 989     989 0 11265 sub get_sock { shift->{sock} }
30 91     91 0 284 sub get_timeout { shift->{timeout} }
31 76     76 0 316 sub get_classes { shift->{classes} }
32 152     152 0 510 sub get_class_map { shift->{class_map} }
33 152     152 0 494 sub get_loaded_classes { shift->{loaded_classes} }
34 7     7 0 56 sub get_error_cb { shift->{error_cb} }
35 91     91 0 358 sub get_ssl { shift->{ssl} }
36 12     12 0 51 sub get_ssl_ca_file { shift->{ssl_ca_file} }
37 7     7 0 40 sub get_ssl_ca_path { shift->{ssl_ca_path} }
38 7     7 0 17 sub get_ssl_opts { shift->{ssl_opts} }
39 79     79 0 207 sub get_auth_user { shift->{auth_user} }
40 79     79 0 168 sub get_auth_pass { shift->{auth_pass} }
41 301     301 0 64976 sub get_connected { shift->{connected} }
42 91     91 0 332 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 24427 sub get_message_format { shift->{message_format} }
46 76     76 0 1908 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 923 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 6960 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 12 sub set_auth_pass { shift->{auth_pass} = $_[1] }
62 229     229 0 1568 sub set_connected { shift->{connected} = $_[1] }
63 0     0 0 0 sub set_server { shift->{server} = $_[1] }
64 79     79 0 865 sub set_server_version { shift->{server_version} = $_[1] }
65 79     79 0 706 sub set_server_protocol { shift->{server_protocol} = $_[1] }
66 153     153 0 428 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 1504 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 53752 my $class = shift;
81 87         1531 my %par = @_;
82             my ($server, $host, $port, $classes, $class_map, $error_cb, $timeout) =
83 87         749 @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         832 @par{'ssl','ssl_ca_file','ssl_opts','auth_user','auth_pass','insecure_msg_fmt_ok'};
86              
87 87   50     2387 $server ||= '';
88 87   50     856 $host ||= '';
89 87 100       936 $insecure_msg_fmt_ok = 1 unless defined $insecure_msg_fmt_ok;
90              
91 87 50 33     908 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         6420 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         700 return $self;
116             }
117              
118             sub connect {
119 91     91 1 805 my $self = shift;
120              
121 91 50       1862 croak "Client is already connected" if $self->get_connected;
122              
123 91         1190 my $ssl = $self->get_ssl;
124 91         413 my $server = $self->get_server;
125 91         698 my $port = $self->get_port;
126 91         557 my $timeout = $self->get_timeout;
127              
128 91         489 $self->set_message_format($Event::RPC::Client::DEFAULT_MESSAGE_FORMAT);
129              
130             #-- Client may try to fallback to Storable
131 91 100 100     412 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       414 if ( $ssl ) {
136 7         21 eval { require IO::Socket::SSL };
  7         114  
137 7 50       33 croak "SSL requested, but IO::Socket::SSL not installed" if $@;
138             }
139              
140 91         206 my $sock;
141 91 100       305 if ( $ssl ) {
142 7         13 my @verify_opts;
143 7 100 66     32 if ( $self->get_ssl_ca_file or $self->get_ssl_ca_path ) {
144 5         16 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         6 push @verify_opts, (
152             SSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),
153             );
154             }
155              
156 7         28 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       229 ($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             else {
170 84 50       714 $sock = IO::Socket::INET->new(
171             Proto => 'tcp',
172             PeerPort => $port,
173             PeerAddr => $server,
174             Type => SOCK_STREAM,
175             Timeout => $timeout,
176             )
177             or croak "Can't open connection to $server:$port - $!";
178             }
179              
180 89         99444 $sock->autoflush(1);
181              
182 89         4520 $self->set_sock($sock);
183              
184 89         900 eval {
185             #-- Perform message format negotitation if we are not
186             #-- configured to a specific format already.
187 89 100       333 $self->negotiate_message_format
188             if $self->get_message_format eq 'Event::RPC::Message::Negotiate';
189              
190 86         763 $self->check_version;
191             };
192              
193 89 100       327 if ( $@ ) {
194 10         72 $self->disconnect;
195 10         88 die $@;
196             }
197              
198 79         336 my $auth_user = $self->get_auth_user;
199 79         237 my $auth_pass = $self->get_auth_pass;
200              
201 79 100       244 if ( $auth_user ) {
202 7         58 my $rc = $self->send_request(
203             { cmd => 'auth',
204             user => $auth_user,
205             pass => $auth_pass,
206             }
207             );
208 4 50       35 if ( not $rc->{ok} ) {
209 0         0 $self->disconnect;
210 0         0 croak $rc->{msg};
211             }
212             }
213              
214 76 50       343 if ( not $self->get_classes ) {
215 76         685 $self->load_all_classes;
216             }
217             else {
218 0         0 $self->load_classes;
219             }
220              
221 76         319 $self->set_connected(1);
222              
223 76         244 1;
224             }
225              
226             sub log_connect {
227 174     174 0 48209359 my $class = shift;
228 174         4589 my %par = @_;
229 174         2015 my ( $server, $port ) = @par{ 'server', 'port' };
230              
231 174 100       6914 my $sock = IO::Socket::INET->new(
232             Proto => 'tcp',
233             PeerPort => $port,
234             PeerAddr => $server,
235             Type => SOCK_STREAM
236             )
237             or croak "Can't open connection to $server:$port - $!";
238              
239 87         69635 return $sock;
240             }
241              
242             sub disconnect {
243 153     153 1 15509 my $self = shift;
244              
245 153 100       474 close( $self->get_sock ) if $self->get_sock;
246 153         1758 $self->set_connected(0);
247              
248 153         2216 1;
249             }
250              
251             sub DESTROY {
252 67     67   10167540 shift->disconnect;
253             }
254              
255             sub error {
256 7     7 0 56 my $self = shift;
257 7         35 my ($message) = @_;
258              
259 7         91 my $error_cb = $self->get_error_cb;
260              
261 7 50       70 if ($error_cb) {
262 0         0 &$error_cb( $self, $message );
263             }
264             else {
265 7         56 die "Unhandled error in client/server communication: $message";
266             }
267              
268 0         0 1;
269             }
270              
271             sub negotiate_message_format {
272 65     65 0 171 my $self = shift;
273              
274 65         175 my $rc = eval {
275 65         1528 $self->send_request({
276             cmd => "neg_formats_avail"
277             })
278             };
279              
280 65 100       584 if ( $@ ) {
281             #-- On error we probably may fall back to Storable
282             #-- (we connected to an old server)
283 9 100       63 if ( $self->get_insecure_msg_fmt_ok ) {
284 6         30 require Event::RPC::Message::Storable;
285 6         36 $self->set_message_format("Event::RPC::Message::Storable");
286 6         30 return;
287             }
288              
289             #-- die if Storable is not allowed
290 3         42 die "Error on message format negotiation and client is not ".
291             "allowed to fall back to Storable\n";
292             }
293              
294 56         316 my $modules_by_format_name =
295             Event::RPC::Message::Negotiate->known_message_formats;
296              
297 56         500 my @formats = split(/,/, $rc->{msg});
298              
299 56         291 my $format_chosen = '';
300 56         402 my $module_chosen = '';
301 56         525 foreach my $format ( @formats ) {
302 56 50       457 my $module = $modules_by_format_name->{$format}
303             or die "Unknown message format '$format";
304              
305 56     20   8960 eval "use $module";
  20         6036  
  20         62  
  20         506  
306              
307 56 50       339 if ( not $@ ) {
308 56         160 $format_chosen = $format;
309 56         120 $module_chosen = $module;
310 56         153 last;
311             };
312             }
313              
314 56 50       188 die "Can't negotiate message format\n" unless $format_chosen;
315              
316 56         161 eval {
317 56         361 $self->send_request({
318             cmd => "neg_format_set",
319             msg => $format_chosen,
320             })
321             };
322              
323 56 50       240 die "Error on neg_format_set: $@" if $@;
324              
325 56         239 $self->set_message_format($module_chosen);
326              
327 56         179 1;
328             }
329              
330             sub check_version {
331 86     86 0 220 my $self = shift;
332              
333 86         282 my $rc = eval { $self->send_request( { cmd => 'version', } ) };
  86         924  
334 86 100       493 die "CATCHED $@" if $@;
335              
336 79         465 $self->set_server_version( $rc->{version} );
337 79         428 $self->set_server_protocol( $rc->{protocol} );
338              
339 79 50       433 if ( $rc->{version} ne $self->get_client_version ) {
340 0         0 warn "Event::RPC warning: server version $rc->{version} != "
341             . "client version "
342             . $self->get_client_version;
343             }
344              
345 79 50       690 if ( $rc->{protocol} < $self->get_client_protocol ) {
346 0         0 die "FATAL: Server protocol version $rc->{protocol} < "
347             . "client protocol version "
348             . $self->get_client_protocol;
349             }
350              
351 79         401 1;
352             }
353              
354             sub load_all_classes {
355 76     76 0 221 my $self = shift;
356              
357 76         574 my $rc = $self->send_request( { cmd => 'class_info_all', } );
358              
359 76         265 my $class_info_all = $rc->{class_info_all};
360              
361 76         459 foreach my $class ( keys %{$class_info_all} ) {
  76         935  
362 152         823 $self->load_class( $class, $class_info_all->{$class} );
363             }
364              
365 76         450 1;
366             }
367              
368             sub load_classes {
369 0     0 0 0 my $self = shift;
370              
371 0         0 my $classes = $self->get_classes;
372 0         0 my %classes;
373 0         0 @classes{ @{$classes} } = (1) x @{$classes};
  0         0  
  0         0  
374              
375 0         0 my $rc = $self->send_request( { cmd => 'classes_list', } );
376              
377 0         0 foreach my $class ( @{ $rc->{classes} } ) {
  0         0  
378 0 0       0 next if not $classes{$class};
379 0         0 $classes{$class} = 0;
380              
381 0         0 my $rc = $self->send_request(
382             { cmd => 'class_info',
383             class => $class,
384             }
385             );
386              
387 0         0 $self->load_class( $class, $rc->{methods} );
388             }
389              
390 0         0 foreach my $class ( @{$classes} ) {
  0         0  
391             warn "WARNING: Class '$class' not exported by server"
392 0 0       0 if $classes{$class};
393             }
394              
395 0         0 1;
396             }
397              
398             sub load_class {
399 152     152 0 367 my $self = shift;
400 152         382 my ( $class, $methods ) = @_;
401              
402 152         659 my $loaded_classes = $self->get_loaded_classes;
403 152 50       535 return 1 if $loaded_classes->{$class};
404 152         351 $loaded_classes->{$class} = 1;
405              
406 152         236 my $local_method;
407 152         606 my $class_map = $self->get_class_map;
408 152   33     1259 my $local_class = $class_map->{$class} || $class;
409              
410             # create local destructor for this class
411             {
412 31     31   82114 no strict 'refs';
  31         60  
  31         3697  
  152         334  
413 152         383 my $local_method = $local_class . '::' . "DESTROY";
414             *$local_method = sub {
415 165 100   165   28714 return if not $self->get_connected;
416 34         80 my $oid_ref = shift;
417             $self->send_request({
418             cmd => "client_destroy",
419 34         69 oid => ${$oid_ref},
  34         181  
420             });
421 152         3305 };
422             }
423              
424             # create local methods for this class
425 152         482 foreach my $method ( keys %{$methods} ) {
  152         840  
426 1520         3742 $local_method = $local_class . '::' . $method;
427              
428 1520         2601 my $method_type = $methods->{$method};
429              
430 1520 100       3531 if ( $method_type eq '_constructor' ) {
    100          
431             # this is a constructor for this class
432 228         743 my $request_method = $class . '::' . $method;
433 31     31   163 no strict 'refs';
  31         75  
  31         2709  
434             *$local_method = sub {
435 40     40   3314 shift;
436 40         505 my $rc = $self->send_request({
437             cmd => 'new',
438             method => $request_method,
439             params => \@_,
440             });
441 40         140 my $oid = $rc->{oid};
442 40         210 return bless \$oid, $local_class;
443 228         2679 };
444             }
445             elsif ( $method_type eq '1' ) {
446             # this is a simple method
447 836         1380 my $request_method = $method;
448 31     31   173 no strict 'refs';
  31         54  
  31         2961  
449             *$local_method = sub {
450 249     249   45804 my $oid_ref = shift;
451             my $rc = $self->send_request({
452             cmd => 'exec',
453 249         405 oid => ${$oid_ref},
  249         1725  
454             method => $request_method,
455             params => \@_,
456             });
457 248 50       989 return unless $rc;
458 248         587 $rc = $rc->{rc};
459 248 100       517 return @{$rc} if wantarray;
  1         4  
460 247         2923 return $rc->[0];
461 836         7121 };
462             }
463             else {
464             # this is a object returner
465 456         1840 my $request_method = $method;
466 31     31   175 no strict 'refs';
  31         57  
  31         13688  
467             *$local_method = sub {
468 71     71   494 my $oid_ref = shift;
469             my $rc = $self->send_request({
470             cmd => 'exec',
471 71         163 oid => ${$oid_ref},
  71         524  
472             method => $request_method,
473             params => \@_,
474             });
475 71 50       389 return unless $rc;
476 71         230 $rc = $rc->{rc};
477              
478 71         127 foreach my $val ( @{$rc} ) {
  71         208  
479 72 100       382 if ( ref $val eq 'ARRAY' ) {
    100          
    100          
480 1         2 foreach my $list_elem ( @{$val} ) {
  1         2  
481 10         17 my ($class) = split( "=", "$list_elem", 2 );
482             $self->load_class($class)
483 10 50       16 unless $loaded_classes->{$class};
484 10         12 my $list_elem_copy = $list_elem;
485 10         10 $list_elem = \$list_elem_copy;
486             bless $list_elem,
487 10   33     38 ( $class_map->{$class} || $class );
488             }
489             }
490             elsif ( ref $val eq 'HASH' ) {
491 1         2 foreach my $hash_elem ( values %{$val} ) {
  1         9  
492 10         19 my ($class) = split( "=", "$hash_elem", 2 );
493             $self->load_class($class)
494 10 50       14 unless $loaded_classes->{$class};
495 10         11 my $hash_elem_copy = $hash_elem;
496 10         10 $hash_elem = \$hash_elem_copy;
497             bless $hash_elem,
498 10   33     28 ( $class_map->{$class} || $class );
499             }
500             }
501             elsif ( defined $val ) {
502 69         378 my ($class) = split( "=", "$val", 2 );
503             $self->load_class($class)
504 69 50       275 unless $loaded_classes->{$class};
505 69         147 my $val_copy = $val;
506 69         161 $val = \$val_copy;
507 69   33     1009 bless $val, ( $class_map->{$class} || $class );
508             }
509             }
510 71 100       222 return @{$rc} if wantarray;
  1         5  
511 70         302 return $rc->[0];
512 456         6110 };
513             }
514             }
515              
516 152         584 return $local_class;
517             }
518              
519             sub send_request {
520 684     684 0 1484 my $self = shift;
521 684         1283 my ($request) = @_;
522              
523 684         1659 my $message = $self->get_message_format->new( $self->get_sock );
524              
525 684         3154 $message->write_blocked($request);
526              
527 684         1094 my $rc = eval { $message->read_blocked };
  684         2244  
528              
529 684 100       4599 if ($@) {
530 7         175 $self->error($@);
531 0         0 return;
532             }
533              
534 677 100       1668 if ( not $rc->{ok} ) {
535 13 100       120 $rc->{msg} .= "\n" if not $rc->{msg} =~ /\n$/;
536 13         3015 croak ("$rc->{msg} -- called via Event::RPC::Client");
537             }
538              
539 664         3423 return $rc;
540             }
541              
542             1;
543              
544             __END__