File Coverage

lib/XML/Compile/RPC/Client.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Copyrights 2009-2013 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.01.
5 4     4   3781 use warnings;
  4         7  
  4         121  
6 4     4   20 use strict;
  4         5  
  4         178  
7              
8             package XML::Compile::RPC::Client;
9 4     4   20 use vars '$VERSION';
  4         8  
  4         248  
10             $VERSION = '0.17';
11              
12              
13 4     4   1951 use XML::Compile::RPC ();
  0            
  0            
14             use XML::Compile::RPC::Util qw/fault_code/;
15              
16             use Log::Report 'xml-compile-rpc', syntax => 'LONG';
17             use Time::HiRes qw/gettimeofday tv_interval/;
18             use HTTP::Request ();
19             use LWP::UserAgent ();
20              
21              
22             sub new(@) { my $class = shift; (bless {}, $class)->init({@_}) }
23              
24             sub init($)
25             { my ($self, $args) = @_;
26             $self->{user_agent} = $args->{user_agent} || LWP::UserAgent->new;
27             $self->{xmlformat} = $args->{xmlformat} || 0;
28             $self->{auto_under} = $args->{autoload_underscore_is};
29             $self->{destination} = $args->{destination}
30             or report ERROR => __x"client requires a destination parameter";
31              
32             # convert header template into header object
33             my $headers = $args->{http_header};
34             $headers = HTTP::Headers->new(@{$headers || []})
35             unless UNIVERSAL::isa($headers, 'HTTP::Headers');
36              
37             # be sure we have a content-type
38             $headers->content_type
39             or $headers->content_type('text/xml');
40              
41             $self->{headers} = $headers;
42             $self->{schemas} = $args->{schemas} ||= XML::Compile::RPC->new;
43             $self;
44             }
45              
46              
47             sub headers() {shift->{headers}}
48              
49              
50             sub schemas() {shift->{schemas}}
51              
52              
53             my %trace;
54             sub trace() {\%trace}
55              
56              
57             sub printTrace(;$)
58             { my $self = shift;
59             my $fh = shift || \*STDERR;
60              
61             $fh->print("response: ",$trace{response}->status_line, "\n");
62             $fh->print("elapse: $trace{total_elapse}\n");
63             }
64              
65              
66             sub call($@)
67             { my $self = shift;
68             my $start = [gettimeofday];
69             my $request = $self->_request($self->_callmsg(@_));
70             my $format = [gettimeofday];
71             my $response = $self->{user_agent}->request($request);
72             my $network = [gettimeofday];
73            
74             %trace =
75             ( request => $request
76             , response => $response
77             , start_time => ($start->[0] + $start->[1]*10e-6)
78             , format_elapse => tv_interval($start, $format)
79             , network_elapse => tv_interval($format, $network)
80             );
81              
82             $response->is_success
83             or return ($response->code, $response->status_line);
84              
85             my ($rc, $decoded) = $self->_respmsg($response->decoded_content);
86             $trace{decode_elapse} = tv_interval $network;
87             $trace{total_elapse} = tv_interval $start;
88              
89             ($rc, $decoded);
90             }
91              
92             sub _callmsg($@)
93             { my ($self, $method) = (shift, shift);
94              
95             my @params;
96             while(@_)
97             { my $type = shift;
98             my $value = UNIVERSAL::isa($type, 'HASH') ? $type : {$type => shift};
99             push @params, { value => $value };
100             }
101              
102             my $doc = XML::LibXML::Document->new('1.0', 'UTF-8');
103             my $xml = $self->{schemas}->writer('methodCall')->($doc
104             , { methodName => $method, params => { param => \@params }});
105             $doc->setDocumentElement($xml);
106             $doc;
107             }
108              
109             sub _request($)
110             { my ($self, $doc) = @_;
111             HTTP::Request->new
112             ( POST => $self->{destination}
113             , $self->{headers}
114             , $doc->toString($self->{xmlformat})
115             );
116             }
117              
118             sub _respmsg($)
119             { my ($self, $xml) = @_;
120             length $xml or return (1, "no xml received");
121             my $data = $self->{schemas}->reader('methodResponse')->($xml);
122             return fault_code $data->{fault}
123             if $data->{fault};
124              
125             my ($type, $value) = %{$data->{params}{param}{value}};
126             (0, $value);
127             }
128              
129             sub AUTOLOAD
130             { my $self = shift;
131             (my $proc = our $AUTOLOAD) =~ s/.*\:\://;
132             $proc =~ s/_/$self->{auto_under}/g
133             if defined $self->{auto_under};
134             $self->call($proc, @_);
135             }
136              
137             sub DESTROY {} # avoid DESTROY to AUTOLOAD
138              
139             1;
140              
141             __END__