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