/usr/share/perl5/XML/RPC/Fast.pm is in libxml-rpc-fast-perl 0.8-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | # XML::RPC::Fast
#
# Copyright (c) 2008-2009 Mons Anderson <mons@cpan.org>, all rights reserved
# This program is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
package XML::RPC::Fast;
=head1 NAME
XML::RPC::Fast - Fast and modular implementation for an XML-RPC client and server
=cut
our $VERSION = '0.8'; $VERSION = eval $VERSION;
=head1 SYNOPSIS
Generic usage
use XML::RPC::Fast;
my $server = XML::RPC::Fast->new( undef, %args );
my $client = XML::RPC::Fast->new( $uri, %args );
Create a simple XML-RPC service:
use XML::RPC::Fast;
my $rpc = XML::RPC::Fast->new(
undef, # the url is not required by server
external_encoding => 'koi8-r', # any encoding, accepted by Encode
#internal_encoding => 'koi8-r', # not supported for now
);
my $xml = do { local $/; <STDIN> };
length($xml) == $ENV{CONTENT_LENGTH} or warn "Content-Length differs from actually received";
print "Content-type: text/xml; charset=$rpc->{external_encoding}\n\n";
print $rpc->receive( $xml, sub {
my ( $methodname, @params ) = @_;
return { you_called => $methodname, with_params => \@params };
} );
Make a call to an XML-RPC service:
use XML::RPC::Fast;
my $rpc = XML::RPC::Fast->new(
'http://your.hostname/rpc/url'
);
# Syncronous call
my @result = $rpc->req(
call => [ 'examples.getStateStruct', { state1 => 12, state2 => 28 } ],
url => 'http://...',
);
# Syncronous call (compatibility method)
my @result = $rpc->call( 'examples.getStateStruct', { state1 => 12, state2 => 28 } );
# Syncronous or asyncronous call
$rpc->req(
call => ['examples.getStateStruct', { state1 => 12, state2 => 28 }],
cb => sub {
my @result = @_;
},
);
# Syncronous or asyncronous call (compatibility method)
$rpc->call( sub {
my @result = @_;
}, 'examples.getStateStruct', { state1 => 12, state2 => 28 } );
=head1 DESCRIPTION
XML::RPC::Fast is format-compatible with XML::RPC, but may use different encoders to parse/compose xml.
Curerntly included encoder uses L<XML::LibXML>, and is 3 times faster than XML::RPC and 75% faster, than XML::Parser implementation
=head1 METHODS
=head2 new ($url, %args)
Create XML::RPC::Fast object, server if url is undef, client if url is defined
=head2 req( %ARGS )
Clientside. Make syncronous or asyncronous call (depends on UA).
If have cb, will invoke $cb with results and should not croak
If have no cb, will return results and croak on error (only syncronous UA)
Arguments are
=over 4
=item call => [ methodName => @args ]
array ref of call arguments. Required
=item cb => $cb->(@results)
Invocation callback. Optional for syncronous UA. Behaviour is same as in call with C<$cb> and without
=item url => $request_url
Alternative invocation URL. Optional. By default will be used defined from constructor
=item headers => { http-headers hashref }
Additional http headers to request
=item external_encoding => '...,
Specify the encoding, used inside XML container just for this request. Passed to encoder
=back
=head2 call( 'method_name', @arguments ) : @results
Clientside. Make syncronous call and return results. Croaks on error. Just a simple wrapper around C<req>
=head2 call( $cb->(@res), 'method_name', @arguments ): void
Clientside. Make syncronous or asyncronous call (depends on UA) and invoke $cb with results. Should not croak. Just a simple wrapper around C<req>
=head2 receive ( $xml, $handler->($methodName,@args) ) : xml byte-stream
Serverside. Process received XML and invoke $handler with parameters $methodName and @args and returns response XML
On error conditions C<$handler> could set C<$XML::RPC::Fast::faultCode> and die, or return C<rpcfault($faultCode,$faultString)>
->receive( $xml, sub {
# ...
return rpcfault( 3, "Some error" ) if $error_condition
$XML::RPC::Fast::faultCode = 4 and die "Another error" if $another_error_condition;
return { call => $methodname, params => \@params };
})
=head2 registerType
Proxy-method to encoder. See L<XML::RPC::Enc>
=head2 registerClass
Proxy-method to encoder. See L<XML::RPC::Enc>
=head1 OPTIONS
Below is the options, accepted by new()
=head2 ua
Client only. Useragent object, or package name
->new( $url, ua => 'LWP' ) # same as XML::RPC::UA::LWP
# or
->new( $url, ua => 'XML::RPC::UA::LWP' )
# or
->new( $url, ua => XML::RPC::UA::LWP->new( ... ) )
# or
->new( $url, ua => XML::RPC::UA::Curl->new( ... ) )
=head2 timeout
Client only. Timeout for calls. Passed directly to UA
->new( $url, ua => 'LWP', timeout => 10 )
=head2 useragent
Client only. Useragent string. Passed directly to UA
->new( $url, ua => 'LWP', useragent => 'YourClient/1.11' )
=head2 encoder
Client and server. Encoder object or package name
->new( $url, encoder => 'LibXML' )
# or
->new( $url, encoder => 'XML::RPC::Enc::LibXML' )
# or
->new( $url, encoder => XML::RPC::Enc::LibXML->new( ... ) )
=head2 internal_encoding B<NOT IMPLEMENTED YET>
Specify the encoding you are using in your code. By default option is undef, which means flagged utf-8
For translations is used Encode, so the list of accepted encodings fully derived from it.
=head2 external_encoding
Specify the encoding, used inside XML container. By default it's utf-8. Passed directly to encoder
->new( $url, encoder => 'LibXML', external_encoding => 'koi8-r' )
=head1 ACCESSORS
=head2 url
Get or set client url
=head2 encoder
Direct access to encoder object
=head2 ua
Direct access to useragent object
=head1 FUNCTIONS
=head2 rpcfault(faultCode, faultString)
Returns hash structure, that may be returned by serverside handler, instead of die. Not exported by default
=head1 CUSTOM TYPES
=head2 sub {{ 'base64' => encode_base64($data) }}
When passing a CODEREF as a value, encoder will simply use the returned hashref as a type => value pair.
=head2 bless( do{\(my $o = encode_base64('test') )}, 'base64' )
When passing SCALARREF as a value, package name will be taken as type and dereference as a value
=head2 bless( do{\(my $o = { something =>'complex' } )}, 'base64' )
When passing REFREF as a value, package name will be taken as type and L<XML::Hash::LX>C<::hash2xml(deref)> would be used as value
=head2 customtype( $type, $data )
Easily compose SCALARREF based custom type
=cut
use 5.008003; # I want Encode to work
use strict;
use warnings;
#use Time::HiRes qw(time);
use Carp qw(carp croak);
BEGIN {
eval {
require Sub::Name;
Sub::Name->import('subname');
1 } or do { *subname = sub { $_[1] } };
no strict 'refs';
for my $m (qw(url encoder ua)) {
*$m = sub {
local *__ANON__ = $m;
my $self = shift;
$self->{$m} = shift if @_;
$self->{$m};
};
}
}
our $faultCode = 0;
#sub encoder { shift->{encoder} }
#sub ua { shift->{ua} }
sub import {
my $me = shift;
my $pkg = caller;
no strict 'refs';
@_ or return;
for (@_) {
if ( $_ eq 'rpcfault' or $_ eq 'customtype') {
*{$pkg.'::'.$_} = \&$_;
} else {
croak "$_ is not exported by $me";
}
}
}
sub rpcfault($$) {
my ($code,$string) = @_;
return {
fault => {
faultCode => $code,
faultString => $string,
},
}
}
sub customtype($$) {
my $type = shift;
my $data = shift;
bless( do{\(my $o = $data )}, $type )
}
sub _load {
my $pkg = shift;
my ($prefix,$req,$default,@args) = @_;
if (defined $req) {
my @fail;
eval {
require join '/', split '::', $prefix.$req.'.pm';
$req = $prefix.$req;
1;
}
or do {
push @fail, [ $prefix.$req,$@ ];
eval{ require join '/', split '::', $req.'.pm'; 1 }
}
or do {
push @fail, [ $req,$@ ];
croak "Can't load any of:\n".join("\n\t",map { "$$_[0]: $$_[1]" } @fail)."\n";
}
} else {
eval {
$req = $prefix.$default;
require join '/', split '::', $req.'.pm'; 1
}
or do {
croak "Can't load $req: $@\n";
}
}
return $req->new(@args);
}
sub new {
my $package = shift;
my $url = shift;
local $SIG{__WARN__} = sub { local $_ = shift; s{\n$}{};carp $_ };
my $self = {
@_,
};
unless ( ref $self->{encoder} ) {
$self->{encoder} = $package->_load(
'XML::RPC::Enc::', $self->{encoder}, 'LibXML',
internal_encoding => $self->{internal_encoding},
external_encoding => $self->{external_encoding},
);
}
if ( $url and !ref $self->{ua} ) {
$self->{ua} = $package->_load(
'XML::RPC::UA::', $self->{ua}, 'LWP',
ua => $self->{useragent} || 'XML-RPC-Fast/'.$VERSION,
timeout => $self->{timeout},
);
}
$self->{url} = $url;
bless $self, $package;
return $self;
}
sub registerType {
shift->encoder->registerType(@_);
}
sub registerClass {
shift->encoder->registerClass(@_);
}
sub call {
my $self = shift;
my $cb;$cb = shift if ref $_[0] and ref $_[0] eq 'CODE';
$self->req(
call => [@_],
$cb ? ( cb => $cb ) : (),
);
}
sub req {
my $self = shift;
my %args = @_;
my $cb = $args{cb};
if ($self->ua->async and !$cb) {
croak("Call have no cb and useragent is async");
}
my ( $methodname, @params ) = @{ $args{call} };
my $url = $args{url} || $self->{url};
unless ( $url ) {
if ($cb) {
$cb->(rpcfault(500, "No url"));
return;
} else {
croak('No url');
}
};
my $uri = "$url#$methodname";
$faultCode = 0;
my $body;
{
local $self->encoder->{external_encoding} = $args{external_encoding} if exists $args{external_encoding};
my $newurl;
($body,$newurl) = $self->encoder->request( $methodname, @params );
$url = $newurl if defined $newurl;
}
$self->{xml_out} = $body;
#my $start = time;
my @data;
#warn "Call $body";
$self->ua->call(
($args{method} || 'POST') => $url,
$args{headers} ? ( headers => $args{headers} ) : (),
body => $body,
cb => sub {
my $res = shift;
{
( my $status = $res->status_line )=~ s/:?\s*$//s;
$res->code == 200 or @data =
(rpcfault( $res->code, "Call to $uri failed: $status" ))
and last;
my $text = $res->content;
length($text) and $text =~ /^\s*<\?xml/s or @data =
({fault=>{ faultCode => 499, faultString => "Call to $uri failed: Response is not an XML: \"$text\"" }})
and last;
eval {
$self->{xml_in} = $text;
@data = $self->encoder->decode( $text );
1;
} or @data =
({fault=>{ faultCode => 499, faultString => "Call to $uri failed: Bad Response: $@, \"$text\"" }})
and last;
}
#warn "Have data @data";
if ($cb) {{
local $faultCode = $data[0]{fault}{faultCode} if ref $data[0] eq 'HASH' and exists $data[0]{fault};
$cb->(@data);
return;
}}
},
);
$cb and defined wantarray and carp "Useless use of return value for ".__PACKAGE__."->call(cb)";
return if $cb;
if ( ref $data[0] eq 'HASH' and exists $data[0]{fault} ) {
$faultCode = $data[0]{fault}{faultCode};
croak( "Remote Error [$data[0]{fault}{faultCode}]: ".$data[0]{fault}{faultString} );
}
return @data == 1 ? $data[0] : @data;
}
sub receive { # ok
my $self = shift;
my $result = eval {
my $xml_in = shift or return $self->encoder->fault(400,"Bad Request: No XML");
my $handler = shift or return $self->encoder->fault(501,"Server Error: No handler");;
my ( $methodname, @params ) = $self->encoder->decode($xml_in);
local $self->{xml_in} = $xml_in;
subname( 'receive.handler.'.$methodname,$handler );
my @res = $handler->( $methodname, @params );
if (ref $res[0] eq 'HASH' and exists $res[0]{fault}) {
$self->encoder->fault( $res[0]{fault}{faultCode},$res[0]{fault}{faultString} );
} else {
$self->encoder->response( @res );
}
};
if ($@) {
(my $e = "$@") =~ s{\r?\n+$}{}s;
$result = $self->encoder->fault(defined $faultCode ? $faultCode : 500,$e);
}
return $result;
}
=head1 BUGS & SUPPORT
Bugs reports and testcases are welcome.
It you write your own Enc or UA, I may include it into distribution
If you have propositions for default custom types (see Enc), send me patches
See L<http://rt.cpan.org> to report and view bugs.
=head1 AUTHOR
Mons Anderson, C<< <mons@cpan.org> >>
=head1 COPYRIGHT & LICENSE
Copyright (c) 2008-2009 Mons Anderson.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1;
|