File Coverage

blib/lib/Protocol/XMLRPC/Dispatcher.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Protocol::XMLRPC::Dispatcher;
2              
3 1     1   23369 use strict;
  1         2  
  1         37  
4 1     1   5 use warnings;
  1         3  
  1         28  
5              
6 1     1   641 use Protocol::XMLRPC::MethodResponse;
  0            
  0            
7             use Protocol::XMLRPC::MethodCall;
8             use Protocol::XMLRPC::ValueFactory;
9              
10             sub new {
11             my $class = shift;
12              
13             my $self = {@_};
14             bless $self, $class;
15              
16             $self->{methods} ||= {};
17             $self->{message_corrupted} ||= 'Method call is corrupted';
18             $self->{message_unknown_method} ||= 'Unknown method';
19             $self->{message_wrong_prototype} ||= 'Wrong prototype';
20              
21             $self->method(
22             'system.getCapabilities' => 'struct' => sub {
23             { name => 'introspect',
24             specUrl =>
25             'http://xmlrpc-c.sourceforge.net/xmlrpc-c/introspection.html',
26             specVersion => 1
27             };
28             }
29             );
30              
31             $self->method('system.listMethods' => 'array' =>
32             sub { [sort keys %{$self->{methods}}]; });
33              
34             $self->method(
35             'system.methodSignature' => 'array' => ['string'] => sub {
36             my ($name) = @_;
37              
38             if (my $method = $self->{methods}->{$name->value}) {
39             return [$method->{ret}, @{$method->{args}}];
40             }
41              
42             die $self->message_unknown_method;
43             }
44             );
45              
46             $self->method(
47             'system.methodHelp' => 'string' => ['string'] => 'Method help' =>
48             sub {
49             my ($name) = @_;
50              
51             if (my $method = $self->{methods}->{$name->value}) {
52             return $method->{descr} || 'Description not available';
53             }
54              
55             die $self->message_unknown_method;
56              
57             }
58             );
59              
60             return $self;
61             }
62              
63             sub method {
64             my $self = shift;
65             my $name = shift;
66             my $ret = shift;
67             my $cb = pop;
68             my $args = ref $_[0] eq 'ARRAY' ? shift : [];
69             my $descr = shift;
70              
71             $self->{methods}->{$name} = {
72             ret => $ret,
73             args => $args,
74             handler => $cb,
75             descr => $descr
76             };
77             }
78              
79             sub methods { defined $_[1] ? $_[0]->{methods} = $_[1] : $_[0]->{methods} }
80              
81             sub message_corrupted {
82             defined $_[1]
83             ? $_[0]->{message_corrupted} = $_[1]
84             : $_[0]->{message_corrupted};
85             }
86              
87             sub message_unknown_method {
88             defined $_[1]
89             ? $_[0]->{message_unknown_method} = $_[1]
90             : $_[0]->{message_unknown_method};
91             }
92              
93             sub message_wrong_prototype {
94             defined $_[1]
95             ? $_[0]->{message_wrong_prototype} = $_[1]
96             : $_[0]->{message_wrong_prototype};
97             }
98              
99             sub dispatch {
100             my $self = shift;
101             my ($xml, $cb) = @_;
102              
103             my $method_response = Protocol::XMLRPC::MethodResponse->new;
104              
105             my $method_call;
106              
107             eval {
108             $method_call = Protocol::XMLRPC::MethodCall->parse($xml);
109             1;
110             } or do {
111             $method_response->fault(-1 => $self->message_corrupted);
112             return $cb->($method_response);
113             };
114              
115             my $method_name = $method_call->name;
116              
117             my $method = $self->methods->{$method_name};
118              
119             unless ($method) {
120             $method_response->fault(-1 => $self->message_unknown_method);
121             return $cb->($method_response);
122             }
123              
124             my $params = $method_call->params;
125              
126             my $args_count = @{$method->{args}};
127             my $prototype =
128             $method_call->name . '(' . join(', ', @{$method->{args}}) . ')';
129              
130             unless (@$params == $args_count) {
131             $method_response->fault(-1 => $self->message_wrong_prototype);
132             return $cb->($method_response);
133             }
134              
135             for (my $count = 0; $count < $args_count; $count++) {
136             next if $method->{args}->[$count] eq '*';
137              
138             if ($params->[$count]->type ne $method->{args}->[$count]) {
139             $method_response->fault(-1 => $self->message_wrong_prototype);
140             return $cb->($method_response);
141             }
142             }
143              
144             my $param;
145             eval { $param = $method->{handler}->(@{$method_call->params}) };
146              
147             if ($@) {
148             my ($error) = ($@ =~ m/^(.*?) at/m);
149             $method_response->fault(-1 => $error || 'Internal error');
150             }
151             else {
152             if (my $type = $method->{ret}) {
153             $method_response->param(
154             Protocol::XMLRPC::ValueFactory->build($type => $param));
155             }
156             else {
157             $method_response->param($param);
158             }
159             }
160              
161             return $cb->($method_response);
162             }
163              
164             1;