File Coverage

blib/lib/Lim/RPC/Protocol/XMLRPC.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Lim::RPC::Protocol::XMLRPC;
2              
3 3     3   6393 use common::sense;
  3         9  
  3         32  
4 3     3   203 use Carp;
  3         7  
  3         309  
5              
6 3     3   21 use Scalar::Util qw(blessed weaken);
  3         8  
  3         194  
7              
8 3     3   20 use HTTP::Status qw(:constants);
  3         8  
  3         2877  
9 3     3   22 use HTTP::Request ();
  3         8  
  3         82  
10 3     3   16 use HTTP::Response ();
  3         7  
  3         53  
11              
12 3     3   1347 use XMLRPC::Lite ();
  0            
  0            
13             use XMLRPC::Transport::HTTP::Server ();
14              
15             use Lim ();
16             use Lim::RPC::Callback ();
17              
18             use base qw(Lim::RPC::Protocol);
19              
20             =encoding utf8
21              
22             =head1 NAME
23              
24             ...
25              
26             =head1 VERSION
27              
28             See L for version.
29              
30             =cut
31              
32             our $VERSION = $Lim::VERSION;
33              
34             =head1 SYNOPSIS
35              
36             ...
37              
38             =head1 SUBROUTINES/METHODS
39              
40             =head2 Init
41              
42             =cut
43              
44             sub Init {
45             }
46              
47             =head2 Destroy
48              
49             =cut
50              
51             sub Destroy {
52             my ($self) = @_;
53            
54             delete $self->{xmlrpc};
55             }
56              
57             =head2 name
58              
59             =cut
60              
61             sub name {
62             'xmlrpc';
63             }
64              
65             =head2 serve
66              
67             =cut
68              
69             sub serve {
70             my ($self, $module, $module_shortname) = @_;
71             my ($calls, $tns, $xmlrpc, $obj, $obj_class);
72            
73             $calls = $module->Calls;
74             $tns = $module.'::Server';
75              
76             $xmlrpc = XMLRPC::Transport::HTTP::Server->new;
77             $obj = $self->server->module_obj_by_protocol($module_shortname, $self->name);
78             $obj_class = ref($obj);
79             # TODO: check if $obj_class alread is a XMLRPC::Server::Parameters
80             eval "push(\@${obj_class}::ISA, 'XMLRPC::Server::Parameters');";
81             if ($@) {
82             die $@;
83             }
84             $self->{xmlrpc}->{$module} = $xmlrpc;
85             }
86              
87             =head2 handle
88              
89             =cut
90              
91             sub handle {
92             my ($self, $cb, $request, $transport) = @_;
93            
94             unless (blessed($request) and $request->isa('HTTP::Request')) {
95             return;
96             }
97              
98             if ($request->header('Content-Type') =~ /(?:^|\s)text\/xml(?:$|\s|;)/o and $request->uri =~ /^\/([a-zA-Z]+)\s*$/o) {
99             my ($module) = ($1);
100             my $response = HTTP::Response->new;
101             $response->request($request);
102             $response->protocol($request->protocol);
103            
104             $module = lc($module);
105             my $server = $self->server;
106             if (defined $server and $server->have_module($module) and exists $self->{xmlrpc}->{$server->module_class($module)}) {
107             my ($action, $method_uri, $method_name);
108             my $real_self = $self;
109             my $xmlrpc = $self->{xmlrpc}->{$server->module_class($module)};
110             my $protocol_obj = $server->module_obj_by_protocol($module, $self->name);
111             weaken($self);
112             weaken($xmlrpc);
113              
114             $method_uri = 'urn:'.ref($protocol_obj);
115              
116             Lim::RPC_DEBUG and $self->{logger}->debug('XMLRPC dispatch to module ', $server->module_class($module), ' obj ', $server->module_obj($module), ' proto obj ', $protocol_obj);
117              
118             $xmlrpc->on_dispatch(sub {
119             my ($request) = @_;
120            
121             unless (defined $self and defined $xmlrpc) {
122             return;
123             }
124            
125             $request->{__lim_rpc_protocol_xmlrpc_cb} = Lim::RPC::Callback->new(
126             cb => sub {
127             my ($data) = @_;
128            
129             unless (defined $self and defined $xmlrpc) {
130             return;
131             }
132            
133             if (blessed $data and $data->isa('Lim::Error')) {
134             $xmlrpc->make_fault($data->code, $data->message);
135             }
136             else {
137             my $result;
138            
139             if (defined $data) {
140             $result = $xmlrpc->serializer
141             ->prefix('s')
142             ->uri($method_uri)
143             ->envelope(response => $method_name . 'Response', __xmlrpc_result('base', $data));
144             }
145             else {
146             $result = $xmlrpc->serializer
147             ->prefix('s')
148             ->uri($method_uri)
149             ->envelope(response => $method_name . 'Response');
150             }
151            
152             $xmlrpc->make_response($XMLRPC::Constants::HTTP_ON_SUCCESS_CODE, $result);
153             }
154            
155             $response = $xmlrpc->response;
156             $response->header(
157             'Cache-Control' => 'no-cache',
158             'Pragma' => 'no-cache'
159             );
160            
161             $cb->cb->($response);
162             return;
163             },
164             reset_timeout => sub {
165             $cb->reset_timeout;
166             });
167              
168             unless ($request->method =~ /^\w+$/o) {
169             $request->{__lim_rpc_protocol_xmlrpc_cb}->(Lim::Error->new(500, 'Invalid characters in method name'));
170             return;
171             }
172              
173             return ($method_uri, ($method_name = $request->method));
174             });
175              
176             $xmlrpc->dispatch_to($protocol_obj);
177              
178             eval {
179             $xmlrpc->request($request);
180             $xmlrpc->handle;
181             };
182             if ($@) {
183             Lim::WARN and $self->{logger}->warn('XMLRPC action failed: ', $@);
184             $response->code(HTTP_INTERNAL_SERVER_ERROR);
185             }
186             else {
187             if ($xmlrpc->response) {
188             $cb->cb->($xmlrpc->response);
189             }
190             return 1;
191             }
192             }
193             else {
194             return;
195             }
196              
197             $cb->cb->($response);
198             return 1;
199             }
200             return;
201             }
202              
203             =head2 __xmlrpc_result
204              
205             =cut
206              
207             sub __xmlrpc_result {
208             my @a;
209            
210             foreach my $k (keys %{$_[1]}) {
211             if (ref($_[1]->{$k}) eq 'ARRAY') {
212             foreach my $v (@{$_[1]->{$k}}) {
213             if (ref($v) eq 'HASH') {
214             push(@a,
215             XMLRPC::Data->new->value({ $k => Lim::RPC::__xmlrpc_result($_[0].'.'.$k, $v) })
216             );
217             }
218             else {
219             push(@a,
220             XMLRPC::Data->new->value({ $k => $v })
221             );
222             }
223             }
224             }
225             elsif (ref($_[1]->{$k}) eq 'HASH') {
226             push(@a,
227             XMLRPC::Data->new->value({ $k => Lim::RPC::__xmlrpc_result($_[0].'.'.$k, $_[1]->{$k}) })
228             );
229             }
230             else {
231             push(@a,
232             XMLRPC::Data->new->value({ $k => $_[1]->{$k} })
233             );
234             }
235             }
236              
237             if ($_[0] eq 'base') {
238             return @a;
239             }
240             else {
241             return \@a;
242             }
243             }
244              
245             =head2 precall
246              
247             =cut
248              
249             sub precall {
250             my ($self, $call, $object, $som) = @_;
251            
252             unless (ref($call) eq '' and blessed($object) and blessed($som) and $som->isa('XMLRPC::SOM')) {
253             confess __PACKAGE__, ': Invalid XMLRPC call';
254             }
255              
256             unless (exists $som->{__lim_rpc_protocol_xmlrpc_cb} and blessed($som->{__lim_rpc_protocol_xmlrpc_cb}) and $som->{__lim_rpc_protocol_xmlrpc_cb}->isa('Lim::RPC::Callback')) {
257             confess __PACKAGE__, ': XMLRPC::SOM does not contain lim rpc callback or invalid';
258             }
259             my $cb = delete $som->{__lim_rpc_protocol_xmlrpc_cb};
260             my $valueof = $som->valueof('//'.$call.'/');
261            
262             if ($valueof) {
263             unless (ref($valueof) eq 'HASH') {
264             confess __PACKAGE__, ': Invalid data in XMLRPC call';
265             }
266             }
267             else {
268             undef($valueof);
269             }
270              
271             return ($object, $cb, $valueof);
272             }
273              
274             =head1 AUTHOR
275              
276             Jerry Lundström, C<< >>
277              
278             =head1 BUGS
279              
280             Please report any bugs or feature requests to L.
281              
282             =head1 SUPPORT
283              
284             You can find documentation for this module with the perldoc command.
285              
286             perldoc Lim
287              
288             You can also look for information at:
289              
290             =over 4
291              
292             =item * Lim issue tracker (report bugs here)
293              
294             L
295              
296             =back
297              
298             =head1 ACKNOWLEDGEMENTS
299              
300             =head1 LICENSE AND COPYRIGHT
301              
302             Copyright 2012-2013 Jerry Lundström.
303              
304             This program is free software; you can redistribute it and/or modify it
305             under the terms of either: the GNU General Public License as published
306             by the Free Software Foundation; or the Artistic License.
307              
308             See http://dev.perl.org/licenses/ for more information.
309              
310              
311             =cut
312              
313             1; # End of Lim::RPC::Protocol::XMLRPC