File Coverage

blib/lib/RPC/Any/Server/XMLRPC.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package RPC::Any::Server::XMLRPC;
2 1     1   22357 use Moose;
  0            
  0            
3             use RPC::XML::ParserFactory;
4             use RPC::XML qw(smart_encode);
5              
6             extends 'RPC::Any::Server';
7              
8             has parser => (is => 'rw', isa => 'RPC::XML::Parser', lazy_build => 1);
9             has send_nil => (is => 'rw', isa => 'Bool', default => 0);
10             has '+package_base' => (default => 'RPC::Any::Package::XMLRPC');
11              
12             sub decode_input_to_object {
13             my ($self, $input) = @_;
14             if (!defined $input or $input eq '') {
15             $self->exception("ParseError", "You did not supply any XML to parse.");
16             }
17             local $RPC::XML::ALLOW_NIL = 1;
18             $self->escape_xml($input);
19             my $xml_object = $self->parser->parse($input);
20             if (!blessed $xml_object) {
21             $self->exception('ParseError',
22             "Error while parsing XML-RPC request: $xml_object");
23             }
24             return $xml_object;
25             }
26              
27             sub escape_xml {
28             # High-ASCII characters need to be escaped, or parse() dies.
29             $_[1] =~ s/([\x80-\xFF])/sprintf('&#x%02x;',ord($1))/eg;
30             }
31              
32             sub input_object_to_data {
33             my ($self, $input_object) = @_;
34             my %result = ( method => $input_object->name );
35             my @args;
36             foreach my $arg (@{ $input_object->args }) {
37             push(@args, $arg->value);
38             }
39             $result{arguments} = \@args;
40             return \%result;
41             }
42              
43             sub output_data_to_object {
44             my ($self, $method_result) = @_;
45             local $RPC::XML::ALLOW_NIL = $self->send_nil;
46             $self->handle_undefs($method_result) if !$self->send_nil;
47             my $encoded = smart_encode($method_result);
48             return RPC::XML::response->new($encoded);
49             }
50              
51             sub handle_undefs {
52             my $self = shift;
53             $self->walk_data($_[0], \&_undef_to_string);
54             }
55              
56             sub _undef_to_string {
57             my ($value) = @_;
58             if (!defined $value or eval { $value->isa('RPC::XML::nil') }) {
59             $_[0] = RPC::XML::string->new('');
60             }
61             }
62              
63             sub encode_output_from_object {
64             my ($self, $output_object) = @_;
65             # XXX For some reason, RPC::XML is always returning character strings
66             # instead of byte strings, even when there is no Unicode in the
67             # output.
68             local $RPC::XML::ENCODING = 'UTF-8';
69             return $output_object->as_string;
70             }
71              
72             sub encode_output_from_exception {
73             my ($self, $exception) = @_;
74             my $xmlrpc_error = RPC::XML::fault->new($exception->code,
75             $exception->message);
76             my $return_object = $self->output_data_to_object($xmlrpc_error);
77             return $self->encode_output_from_object($return_object);
78             }
79              
80             sub _build_parser {
81             return RPC::XML::ParserFactory->new();
82             }
83              
84             __PACKAGE__->meta->make_immutable;
85              
86             1;
87              
88             __END__
89              
90             =head1 NAME
91              
92             RPC::Any::Server::XMLRPC - A basic XML-RPC server
93              
94             =head1 SYNOPSIS
95              
96             use RPC::Any::Server::XMLRPC;
97             # Create a server where calling Foo.bar will call My::Module->bar.
98             my $server = RPC::Any::Server::XMLRPC->new(
99             dispatch => { 'Foo' => 'My::Module' },
100             send_nil => 0,
101             );
102             # Read XML from STDIN and print XML result to STDOUT.
103             print $server->handle_input();
104              
105             =head1 DESCRIPTION
106              
107             This is a server that takes I<just> XML-RPC as input, and produces
108             I<just> XML-RPC as output. It doesn't understand HTTP headers or anything
109             like that, and it doesn't produce HTTP headers. For that, see
110             L<RPC::Any::Server::XMLRPC::HTTP> or L<RPC::Any::Server::XMLRPC::CGI>.
111              
112             See L<RPC::Any::Server> for a basic description of how servers
113             work in RPC::Any.
114              
115             Currently, RPC::Any::Server::XMLRPC uses L<RPC::XML> in its backend
116             to parse incoming XML-RPC, and to produce outbound XML-RPC. We
117             do not use the server components of RPC::XML, just the parser.
118              
119             =head1 XMLRPC SERVER ATTRIBUTES
120              
121             These are additional attributes beyond what is specified in
122             L<RPC::Any::Server> that are available for an XML-RPC server.
123             These can all be specified during C<new> or set like
124             C<< $server->method($value) >>. They are all optional.
125              
126             =over
127              
128             =item C<send_nil>
129              
130             There is an extension to the XML-RPC protocol that specifies an
131             additional type of tag, called C<< <nil> >>. The extension is
132             specified at L<http://ontosys.com/xml-rpc/extensions.php>.
133              
134             RPC::Any XMLRPC Servers I<always> understand C<nil> if you
135             send it to them. However, your clients may not understand C<nil>,
136             so this is a boolean that lets you control whether or not
137             RPC::Any::Server::XMLRPC will produce output with C<nil> in it.
138              
139             When C<send_nil> is true, any instance of C<undef> or L<RPC::XML::nil>
140             in a method's return value will be converted to C<< <nil> >>.
141             When C<send_nil> is false, any instance of C<undef> or L<RPC::XML::nil>
142             in a method's return value will be converted to an empty
143             C<< <string> >>.
144              
145             =item C<parser>
146              
147             This is the L<RPC::XML::Parser> instance that RPC::Any::Server:XMLRPC
148             is using internally. Usually you will not have to modify this.
149              
150             =back