rubysl-xmlrpc 1.0.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +14 -6
- data/.travis.yml +5 -6
- data/lib/rubysl/xmlrpc.rb +1 -0
- data/lib/rubysl/xmlrpc/version.rb +1 -1
- data/lib/rubysl/xmlrpc/xmlrpc.rb +301 -0
- data/lib/xmlrpc.rb +1 -0
- data/lib/xmlrpc/.document +1 -0
- data/lib/xmlrpc/README.rdoc +300 -0
- data/lib/xmlrpc/base64.rb +27 -46
- data/lib/xmlrpc/client.rb +320 -343
- data/lib/xmlrpc/config.rb +18 -16
- data/lib/xmlrpc/create.rb +108 -112
- data/lib/xmlrpc/datetime.rb +59 -72
- data/lib/xmlrpc/httpserver.rb +38 -43
- data/lib/xmlrpc/marshal.rb +6 -16
- data/lib/xmlrpc/parser.rb +267 -242
- data/lib/xmlrpc/server.rb +297 -372
- data/lib/xmlrpc/utils.rb +40 -34
- data/rubysl-xmlrpc.gemspec +0 -1
- metadata +16 -24
data/lib/xmlrpc/base64.rb
CHANGED
@@ -1,46 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
4
|
-
|
5
|
-
Released under the same term of license as Ruby.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
You can use (({XMLRPC::Base64})) on the client and server-side as a
|
15
|
-
parameter and/or return-value.
|
16
|
-
|
17
|
-
== Class Methods
|
18
|
-
--- XMLRPC::Base64.new( str, state = :dec )
|
19
|
-
Creates a new (({XMLRPC::Base64})) instance with string ((|str|)) as the
|
20
|
-
internal string. When ((|state|)) is (({:dec})) it assumes that the
|
21
|
-
string ((|str|)) is not in base64 format (perhaps already decoded),
|
22
|
-
otherwise if ((|state|)) is (({:enc})) it decodes ((|str|))
|
23
|
-
and stores it as the internal string.
|
24
|
-
|
25
|
-
--- XMLRPC::Base64.decode( str )
|
26
|
-
Decodes string ((|str|)) with base64 and returns that value.
|
27
|
-
|
28
|
-
--- XMLRPC::Base64.encode( str )
|
29
|
-
Encodes string ((|str|)) with base64 and returns that value.
|
30
|
-
|
31
|
-
== Instance Methods
|
32
|
-
--- XMLRPC::Base64#decoded
|
33
|
-
Returns the internal string decoded.
|
34
|
-
|
35
|
-
--- XMLRPC::Base64#encoded
|
36
|
-
Returns the internal string encoded with base64.
|
37
|
-
|
38
|
-
=end
|
39
|
-
|
40
|
-
module XMLRPC
|
41
|
-
|
1
|
+
#
|
2
|
+
# xmlrpc/base64.rb
|
3
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
4
|
+
#
|
5
|
+
# Released under the same term of license as Ruby.
|
6
|
+
|
7
|
+
module XMLRPC # :nodoc:
|
8
|
+
|
9
|
+
# This class is necessary for 'xmlrpc4r' to determine that a string should
|
10
|
+
# be transmitted base64-encoded and not as a raw-string.
|
11
|
+
#
|
12
|
+
# You can use XMLRPC::Base64 on the client and server-side as a
|
13
|
+
# parameter and/or return-value.
|
42
14
|
class Base64
|
43
|
-
|
15
|
+
|
16
|
+
# Creates a new XMLRPC::Base64 instance with string +str+ as the
|
17
|
+
# internal string. When +state+ is +:dec+ it assumes that the
|
18
|
+
# string +str+ is not in base64 format (perhaps already decoded),
|
19
|
+
# otherwise if +state+ is +:enc+ it decodes +str+
|
20
|
+
# and stores it as the internal string.
|
44
21
|
def initialize(str, state = :dec)
|
45
22
|
case state
|
46
23
|
when :enc
|
@@ -51,20 +28,24 @@ class Base64
|
|
51
28
|
raise ArgumentError, "wrong argument; either :enc or :dec"
|
52
29
|
end
|
53
30
|
end
|
54
|
-
|
31
|
+
|
32
|
+
# Returns the decoded internal string.
|
55
33
|
def decoded
|
56
|
-
@str
|
34
|
+
@str
|
57
35
|
end
|
58
|
-
|
36
|
+
|
37
|
+
# Returns the base64 encoded internal string.
|
59
38
|
def encoded
|
60
39
|
Base64.encode(@str)
|
61
40
|
end
|
62
41
|
|
63
42
|
|
43
|
+
# Decodes string +str+ with base64 and returns that value.
|
64
44
|
def Base64.decode(str)
|
65
45
|
str.gsub(/\s+/, "").unpack("m")[0]
|
66
46
|
end
|
67
47
|
|
48
|
+
# Encodes string +str+ with base64 and returns that value.
|
68
49
|
def Base64.encode(str)
|
69
50
|
[str].pack("m")
|
70
51
|
end
|
@@ -77,5 +58,5 @@ end # module XMLRPC
|
|
77
58
|
|
78
59
|
=begin
|
79
60
|
= History
|
80
|
-
$Id
|
61
|
+
$Id$
|
81
62
|
=end
|
data/lib/xmlrpc/client.rb
CHANGED
@@ -1,302 +1,90 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
* ((<XMLRPC::Client::Proxy>))
|
10
|
-
|
11
|
-
|
12
|
-
= XMLRPC::Client
|
13
|
-
== Synopsis
|
14
|
-
require "xmlrpc/client"
|
15
|
-
|
16
|
-
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
17
|
-
begin
|
18
|
-
param = server.call("michael.add", 4, 5)
|
19
|
-
puts "4 + 5 = #{param}"
|
20
|
-
rescue XMLRPC::FaultException => e
|
21
|
-
puts "Error:"
|
22
|
-
puts e.faultCode
|
23
|
-
puts e.faultString
|
24
|
-
end
|
25
|
-
|
26
|
-
or
|
27
|
-
|
28
|
-
require "xmlrpc/client"
|
29
|
-
|
30
|
-
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
31
|
-
ok, param = server.call2("michael.add", 4, 5)
|
32
|
-
if ok then
|
33
|
-
puts "4 + 5 = #{param}"
|
34
|
-
else
|
35
|
-
puts "Error:"
|
36
|
-
puts param.faultCode
|
37
|
-
puts param.faultString
|
38
|
-
end
|
39
|
-
|
40
|
-
== Description
|
41
|
-
Class (({XMLRPC::Client})) provides remote procedure calls to a XML-RPC server.
|
42
|
-
After setting the connection-parameters with ((<XMLRPC::Client.new>)) which
|
43
|
-
creates a new (({XMLRPC::Client})) instance, you can execute a remote procedure
|
44
|
-
by sending the ((<call|XMLRPC::Client#call>)) or ((<call2|XMLRPC::Client#call2>))
|
45
|
-
message to this new instance. The given parameters indicate which method to
|
46
|
-
call on the remote-side and of course the parameters for the remote procedure.
|
47
|
-
|
48
|
-
== Class Methods
|
49
|
-
--- XMLRPC::Client.new( host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=false, timeout =nil)
|
50
|
-
Creates an object which represents the remote XML-RPC server on the
|
51
|
-
given host ((|host|)). If the server is CGI-based, ((|path|)) is the
|
52
|
-
path to the CGI-script, which will be called, otherwise (in the
|
53
|
-
case of a standalone server) ((|path|)) should be (({"/RPC2"})).
|
54
|
-
((|port|)) is the port on which the XML-RPC server listens.
|
55
|
-
If ((|proxy_host|)) is given, then a proxy server listening at
|
56
|
-
((|proxy_host|)) is used. ((|proxy_port|)) is the port of the
|
57
|
-
proxy server.
|
58
|
-
|
59
|
-
Default values for ((|host|)), ((|path|)) and ((|port|)) are 'localhost', '/RPC2' and
|
60
|
-
'80' respectively using SSL '443'.
|
61
|
-
|
62
|
-
If ((|user|)) and ((|password|)) are given, each time a request is send,
|
63
|
-
a Authorization header is send. Currently only Basic Authentification is
|
64
|
-
implemented no Digest.
|
65
|
-
|
66
|
-
If ((|use_ssl|)) is set to (({true})), comunication over SSL is enabled.
|
67
|
-
Note, that you need the SSL package from RAA installed.
|
68
|
-
|
69
|
-
Parameter ((|timeout|)) is the time to wait for a XML-RPC response, defaults to 30.
|
70
|
-
|
71
|
-
--- XMLRPC::Client.new2( uri, proxy=nil, timeout=nil)
|
72
|
-
--- XMLRPC::Client.new_from_uri( uri, proxy=nil, timeout=nil)
|
73
|
-
: uri
|
74
|
-
URI specifying protocol (http or https), host, port, path, user and password.
|
75
|
-
Example: https://user:password@host:port/path
|
76
|
-
|
77
|
-
: proxy
|
78
|
-
Is of the form "host:port".
|
79
|
-
|
80
|
-
: timeout
|
81
|
-
Defaults to 30.
|
82
|
-
|
83
|
-
--- XMLRPC::Client.new3( hash={} )
|
84
|
-
--- XMLRPC::Client.new_from_hash( hash={} )
|
85
|
-
Parameter ((|hash|)) has following case-insensitive keys:
|
86
|
-
* host
|
87
|
-
* path
|
88
|
-
* port
|
89
|
-
* proxy_host
|
90
|
-
* proxy_port
|
91
|
-
* user
|
92
|
-
* password
|
93
|
-
* use_ssl
|
94
|
-
* timeout
|
95
|
-
|
96
|
-
Calls ((<XMLRPC::Client.new>)) with the corresponding values.
|
97
|
-
|
98
|
-
== Instance Methods
|
99
|
-
--- XMLRPC::Client#call( method, *args )
|
100
|
-
Invokes the method named ((|method|)) with the parameters given by
|
101
|
-
((|args|)) on the XML-RPC server.
|
102
|
-
The parameter ((|method|)) is converted into a (({String})) and should
|
103
|
-
be a valid XML-RPC method-name.
|
104
|
-
Each parameter of ((|args|)) must be of one of the following types,
|
105
|
-
where (({Hash})), (({Struct})) and (({Array})) can contain any of these listed ((:types:)):
|
106
|
-
* (({Fixnum})), (({Bignum}))
|
107
|
-
* (({TrueClass})), (({FalseClass})) ((({true})), (({false})))
|
108
|
-
* (({String})), (({Symbol}))
|
109
|
-
* (({Float}))
|
110
|
-
* (({Hash})), (({Struct}))
|
111
|
-
* (({Array}))
|
112
|
-
* (({Date})), (({Time})), (({XMLRPC::DateTime}))
|
113
|
-
* (({XMLRPC::Base64}))
|
114
|
-
* A Ruby object which class includes XMLRPC::Marshallable (only if Config::ENABLE_MARSHALLABLE is (({true}))).
|
115
|
-
That object is converted into a hash, with one additional key/value pair "___class___" which contains the class name
|
116
|
-
for restoring later that object.
|
117
|
-
|
118
|
-
The method returns the return-value from the RPC
|
119
|
-
((-stands for Remote Procedure Call-)).
|
120
|
-
The type of the return-value is one of the above shown,
|
121
|
-
only that a (({Bignum})) is only allowed when it fits in 32-bit and
|
122
|
-
that a XML-RPC (('dateTime.iso8601')) type is always returned as
|
123
|
-
a ((<(({XMLRPC::DateTime}))|URL:datetime.html>)) object and
|
124
|
-
a (({Struct})) is never returned, only a (({Hash})), the same for a (({Symbol})), where
|
125
|
-
always a (({String})) is returned.
|
126
|
-
A (({XMLRPC::Base64})) is returned as a (({String})) from xmlrpc4r version 1.6.1 on.
|
127
|
-
|
128
|
-
If the remote procedure returned a fault-structure, then a
|
129
|
-
(({XMLRPC::FaultException})) exception is raised, which has two accessor-methods
|
130
|
-
(({faultCode})) and (({faultString})) of type (({Integer})) and (({String})).
|
131
|
-
|
132
|
-
--- XMLRPC::Client#call2( method, *args )
|
133
|
-
The difference between this method and ((<call|XMLRPC::Client#call>)) is, that
|
134
|
-
this method do ((*not*)) raise a (({XMLRPC::FaultException})) exception.
|
135
|
-
The method returns an array of two values. The first value indicates if
|
136
|
-
the second value is a return-value ((({true}))) or an object of type
|
137
|
-
(({XMLRPC::FaultException})).
|
138
|
-
Both are explained in ((<call|XMLRPC::Client#call>)).
|
139
|
-
|
140
|
-
Simple to remember: The "2" in "call2" denotes the number of values it returns.
|
141
|
-
|
142
|
-
--- XMLRPC::Client#multicall( *methods )
|
143
|
-
You can use this method to execute several methods on a XMLRPC server which supports
|
144
|
-
the multi-call extension.
|
145
|
-
Example:
|
146
|
-
|
147
|
-
s.multicall(
|
148
|
-
['michael.add', 3, 4],
|
149
|
-
['michael.sub', 4, 5]
|
150
|
-
)
|
151
|
-
# => [7, -1]
|
152
|
-
|
153
|
-
--- XMLRPC::Client#multicall2( *methods )
|
154
|
-
Same as ((<XMLRPC::Client#multicall>)), but returns like ((<XMLRPC::Client#call2>)) two parameters
|
155
|
-
instead of raising an (({XMLRPC::FaultException})).
|
156
|
-
|
157
|
-
--- XMLRPC::Client#proxy( prefix, *args )
|
158
|
-
Returns an object of class (({XMLRPC::Client::Proxy})), initialized with
|
159
|
-
((|prefix|)) and ((|args|)). A proxy object returned by this method behaves
|
160
|
-
like ((<XMLRPC::Client#call>)), i.e. a call on that object will raise a
|
161
|
-
(({XMLRPC::FaultException})) when a fault-structure is returned by that call.
|
162
|
-
|
163
|
-
--- XMLRPC::Client#proxy2( prefix, *args )
|
164
|
-
Almost the same like ((<XMLRPC::Client#proxy>)) only that a call on the returned
|
165
|
-
(({XMLRPC::Client::Proxy})) object behaves like ((<XMLRPC::Client#call2>)), i.e.
|
166
|
-
a call on that object will return two parameters.
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
--- XMLRPC::Client#call_async(...)
|
172
|
-
--- XMLRPC::Client#call2_async(...)
|
173
|
-
--- XMLRPC::Client#multicall_async(...)
|
174
|
-
--- XMLRPC::Client#multicall2_async(...)
|
175
|
-
--- XMLRPC::Client#proxy_async(...)
|
176
|
-
--- XMLRPC::Client#proxy2_async(...)
|
177
|
-
In contrast to corresponding methods without "_async", these can be
|
178
|
-
called concurrently and use for each request a new connection, where the
|
179
|
-
non-asynchronous counterparts use connection-alive (one connection for all requests)
|
180
|
-
if possible.
|
181
|
-
|
182
|
-
Note, that you have to use Threads to call these methods concurrently.
|
183
|
-
The following example calls two methods concurrently:
|
184
|
-
|
185
|
-
Thread.new {
|
186
|
-
p client.call_async("michael.add", 4, 5)
|
187
|
-
}
|
188
|
-
|
189
|
-
Thread.new {
|
190
|
-
p client.call_async("michael.div", 7, 9)
|
191
|
-
}
|
192
|
-
|
193
|
-
|
194
|
-
--- XMLRPC::Client#timeout
|
195
|
-
--- XMLRPC::Client#user
|
196
|
-
--- XMLRPC::Client#password
|
197
|
-
Return the corresponding attributes.
|
198
|
-
|
199
|
-
--- XMLRPC::Client#timeout= (new_timeout)
|
200
|
-
--- XMLRPC::Client#user= (new_user)
|
201
|
-
--- XMLRPC::Client#password= (new_password)
|
202
|
-
Set the corresponding attributes.
|
203
|
-
|
204
|
-
|
205
|
-
--- XMLRPC::Client#set_writer( writer )
|
206
|
-
Sets the XML writer to use for generating XML output.
|
207
|
-
Should be an instance of a class from module (({XMLRPC::XMLWriter})).
|
208
|
-
If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used.
|
209
|
-
|
210
|
-
--- XMLRPC::Client#set_parser( parser )
|
211
|
-
Sets the XML parser to use for parsing XML documents.
|
212
|
-
Should be an instance of a class from module (({XMLRPC::XMLParser})).
|
213
|
-
If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used.
|
214
|
-
|
215
|
-
--- XMLRPC::Client#cookie
|
216
|
-
--- XMLRPC::Client#cookie= (cookieString)
|
217
|
-
Get and set the HTTP Cookie header.
|
218
|
-
|
219
|
-
--- XMLRPC::Client#http_header_extra= (additionalHeaders)
|
220
|
-
Set extra HTTP headers that are included in the request.
|
221
|
-
|
222
|
-
--- XMLRPC::Client#http_header_extra
|
223
|
-
Access the via ((<XMLRPC::Client#http_header_extra=>)) assigned header.
|
224
|
-
|
225
|
-
--- XMLRPC::Client#http_last_response
|
226
|
-
Returns the (({Net::HTTPResponse})) object of the last RPC.
|
227
|
-
|
228
|
-
= XMLRPC::Client::Proxy
|
229
|
-
== Synopsis
|
230
|
-
require "xmlrpc/client"
|
231
|
-
|
232
|
-
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
233
|
-
|
234
|
-
michael = server.proxy("michael")
|
235
|
-
michael2 = server.proxy("michael", 4)
|
236
|
-
|
237
|
-
# both calls should return the same value '9'.
|
238
|
-
p michael.add(4,5)
|
239
|
-
p michael2.add(5)
|
240
|
-
|
241
|
-
== Description
|
242
|
-
Class (({XMLRPC::Client::Proxy})) makes XML-RPC calls look nicer!
|
243
|
-
You can call any method onto objects of that class - the object handles
|
244
|
-
(({method_missing})) and will forward the method call to a XML-RPC server.
|
245
|
-
Don't use this class directly, but use instead method ((<XMLRPC::Client#proxy>)) or
|
246
|
-
((<XMLRPC::Client#proxy2>)).
|
247
|
-
|
248
|
-
== Class Methods
|
249
|
-
--- XMLRPC::Client::Proxy.new( server, prefix, args=[], meth=:call, delim="." )
|
250
|
-
Creates an object which provides (({method_missing})).
|
251
|
-
|
252
|
-
((|server|)) must be of type (({XMLRPC::Client})), which is the XML-RPC server to be used
|
253
|
-
for a XML-RPC call. ((|prefix|)) and ((|delim|)) will be prepended to the methodname
|
254
|
-
called onto this object.
|
255
|
-
|
256
|
-
Parameter ((|meth|)) is the method (call, call2, call_async, call2_async) to use for
|
257
|
-
a RPC.
|
258
|
-
|
259
|
-
((|args|)) are arguments which are automatically given
|
260
|
-
to every XML-RPC call before the arguments provides through (({method_missing})).
|
261
|
-
|
262
|
-
== Instance Methods
|
263
|
-
Every method call is forwarded to the XML-RPC server defined in ((<new|XMLRPC::Client::Proxy#new>)).
|
264
|
-
|
265
|
-
Note: Inherited methods from class (({Object})) cannot be used as XML-RPC names, because they get around
|
266
|
-
(({method_missing})).
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
= History
|
271
|
-
$Id: client.rb 18091 2008-07-16 17:07:44Z shyouhei $
|
272
|
-
|
273
|
-
=end
|
274
|
-
|
275
|
-
|
276
|
-
|
1
|
+
# xmlrpc/client.rb
|
2
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
3
|
+
#
|
4
|
+
# Released under the same term of license as Ruby.
|
5
|
+
#
|
6
|
+
# History
|
7
|
+
# $Id$
|
8
|
+
#
|
277
9
|
require "xmlrpc/parser"
|
278
10
|
require "xmlrpc/create"
|
279
11
|
require "xmlrpc/config"
|
280
12
|
require "xmlrpc/utils" # ParserWriterChooseMixin
|
281
13
|
require "net/http"
|
282
|
-
|
283
|
-
|
284
|
-
|
14
|
+
require "uri"
|
15
|
+
|
16
|
+
module XMLRPC # :nodoc:
|
17
|
+
|
18
|
+
# Provides remote procedure calls to a XML-RPC server.
|
19
|
+
#
|
20
|
+
# After setting the connection-parameters with XMLRPC::Client.new which
|
21
|
+
# creates a new XMLRPC::Client instance, you can execute a remote procedure
|
22
|
+
# by sending the XMLRPC::Client#call or XMLRPC::Client#call2
|
23
|
+
# message to this new instance.
|
24
|
+
#
|
25
|
+
# The given parameters indicate which method to call on the remote-side and
|
26
|
+
# of course the parameters for the remote procedure.
|
27
|
+
#
|
28
|
+
# require "xmlrpc/client"
|
29
|
+
#
|
30
|
+
# server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
31
|
+
# begin
|
32
|
+
# param = server.call("michael.add", 4, 5)
|
33
|
+
# puts "4 + 5 = #{param}"
|
34
|
+
# rescue XMLRPC::FaultException => e
|
35
|
+
# puts "Error:"
|
36
|
+
# puts e.faultCode
|
37
|
+
# puts e.faultString
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# or
|
41
|
+
#
|
42
|
+
# require "xmlrpc/client"
|
43
|
+
#
|
44
|
+
# server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
45
|
+
# ok, param = server.call2("michael.add", 4, 5)
|
46
|
+
# if ok then
|
47
|
+
# puts "4 + 5 = #{param}"
|
48
|
+
# else
|
49
|
+
# puts "Error:"
|
50
|
+
# puts param.faultCode
|
51
|
+
# puts param.faultString
|
52
|
+
# end
|
285
53
|
class Client
|
286
|
-
|
54
|
+
|
287
55
|
USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})"
|
288
56
|
|
289
57
|
include ParserWriterChooseMixin
|
290
58
|
include ParseContentType
|
291
59
|
|
292
60
|
|
293
|
-
#
|
294
|
-
|
295
|
-
|
61
|
+
# Creates an object which represents the remote XML-RPC server on the
|
62
|
+
# given +host+. If the server is CGI-based, +path+ is the
|
63
|
+
# path to the CGI-script, which will be called, otherwise (in the
|
64
|
+
# case of a standalone server) +path+ should be <tt>"/RPC2"</tt>.
|
65
|
+
# +port+ is the port on which the XML-RPC server listens.
|
66
|
+
#
|
67
|
+
# If +proxy_host+ is given, then a proxy server listening at
|
68
|
+
# +proxy_host+ is used. +proxy_port+ is the port of the
|
69
|
+
# proxy server.
|
70
|
+
#
|
71
|
+
# Default values for +host+, +path+ and +port+ are 'localhost', '/RPC2' and
|
72
|
+
# '80' respectively using SSL '443'.
|
73
|
+
#
|
74
|
+
# If +user+ and +password+ are given, each time a request is sent,
|
75
|
+
# an Authorization header is sent. Currently only Basic Authentication is
|
76
|
+
# implemented, no Digest.
|
77
|
+
#
|
78
|
+
# If +use_ssl+ is set to +true+, communication over SSL is enabled.
|
79
|
+
#
|
80
|
+
# Note, that you need the SSL package from RAA installed.
|
81
|
+
#
|
82
|
+
# Parameter +timeout+ is the time to wait for a XML-RPC response, defaults to 30.
|
83
|
+
def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
|
296
84
|
user=nil, password=nil, use_ssl=nil, timeout=nil)
|
297
85
|
|
298
86
|
@http_header_extra = nil
|
299
|
-
@http_last_response = nil
|
87
|
+
@http_last_response = nil
|
300
88
|
@cookie = nil
|
301
89
|
|
302
90
|
@host = host || "localhost"
|
@@ -324,8 +112,7 @@ module XMLRPC
|
|
324
112
|
@proxy_port = @proxy_port.to_i if @proxy_port != nil
|
325
113
|
|
326
114
|
# HTTP object for synchronous calls
|
327
|
-
|
328
|
-
@http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
|
115
|
+
@http = net_http(@host, @port, @proxy_host, @proxy_port)
|
329
116
|
@http.use_ssl = @use_ssl if @use_ssl
|
330
117
|
@http.read_timeout = @timeout
|
331
118
|
@http.open_timeout = @timeout
|
@@ -337,28 +124,55 @@ module XMLRPC
|
|
337
124
|
|
338
125
|
class << self
|
339
126
|
|
127
|
+
# Creates an object which represents the remote XML-RPC server at the
|
128
|
+
# given +uri+. The URI should have a host, port, path, user and password.
|
129
|
+
# Example: https://user:password@host:port/path
|
130
|
+
#
|
131
|
+
# Raises an ArgumentError if the +uri+ is invalid,
|
132
|
+
# or if the protocol isn't http or https.
|
133
|
+
#
|
134
|
+
# If a +proxy+ is given it should be in the form of "host:port".
|
135
|
+
#
|
136
|
+
# The optional +timeout+ defaults to 30 seconds.
|
340
137
|
def new2(uri, proxy=nil, timeout=nil)
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
if proto != "http" and proto != "https"
|
348
|
-
raise "Wrong protocol specified. Only http or https allowed!"
|
349
|
-
end
|
138
|
+
begin
|
139
|
+
url = URI(uri)
|
140
|
+
rescue URI::InvalidURIError => e
|
141
|
+
raise ArgumentError, e.message, e.backtrace
|
142
|
+
end
|
350
143
|
|
351
|
-
|
352
|
-
raise "Wrong
|
144
|
+
unless URI::HTTP === url
|
145
|
+
raise ArgumentError, "Wrong protocol specified. Only http or https allowed!"
|
353
146
|
end
|
354
|
-
|
147
|
+
|
148
|
+
proto = url.scheme
|
149
|
+
user = url.user
|
150
|
+
passwd = url.password
|
151
|
+
host = url.host
|
152
|
+
port = url.port
|
153
|
+
path = url.path.empty? ? nil : url.request_uri
|
154
|
+
|
355
155
|
proxy_host, proxy_port = (proxy || "").split(":")
|
156
|
+
proxy_port = proxy_port.to_i if proxy_port
|
356
157
|
|
357
158
|
self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout)
|
358
159
|
end
|
359
160
|
|
360
161
|
alias new_from_uri new2
|
361
162
|
|
163
|
+
# Receives a Hash and calls XMLRPC::Client.new
|
164
|
+
# with the corresponding values.
|
165
|
+
#
|
166
|
+
# The +hash+ parameter has following case-insensitive keys:
|
167
|
+
# * host
|
168
|
+
# * path
|
169
|
+
# * port
|
170
|
+
# * proxy_host
|
171
|
+
# * proxy_port
|
172
|
+
# * user
|
173
|
+
# * password
|
174
|
+
# * use_ssl
|
175
|
+
# * timeout
|
362
176
|
def new3(hash={})
|
363
177
|
|
364
178
|
# convert all keys into lowercase strings
|
@@ -374,62 +188,128 @@ module XMLRPC
|
|
374
188
|
end
|
375
189
|
|
376
190
|
|
377
|
-
#
|
378
|
-
|
379
|
-
# add additional HTTP headers to the request
|
191
|
+
# Add additional HTTP headers to the request
|
380
192
|
attr_accessor :http_header_extra
|
381
193
|
|
382
|
-
#
|
194
|
+
# Returns the Net::HTTPResponse object of the last RPC.
|
383
195
|
attr_reader :http_last_response
|
384
196
|
|
385
|
-
# Cookie
|
197
|
+
# Get and set the HTTP Cookie header.
|
386
198
|
attr_accessor :cookie
|
387
|
-
|
388
199
|
|
200
|
+
|
201
|
+
# Return the corresponding attributes.
|
389
202
|
attr_reader :timeout, :user, :password
|
390
203
|
|
204
|
+
# Sets the Net::HTTP#read_timeout and Net::HTTP#open_timeout to
|
205
|
+
# +new_timeout+
|
391
206
|
def timeout=(new_timeout)
|
392
207
|
@timeout = new_timeout
|
393
208
|
@http.read_timeout = @timeout
|
394
209
|
@http.open_timeout = @timeout
|
395
210
|
end
|
396
211
|
|
212
|
+
# Changes the user for the Basic Authentication header to +new_user+
|
397
213
|
def user=(new_user)
|
398
214
|
@user = new_user
|
399
215
|
set_auth
|
400
216
|
end
|
401
217
|
|
218
|
+
# Changes the password for the Basic Authentication header to
|
219
|
+
# +new_password+
|
402
220
|
def password=(new_password)
|
403
221
|
@password = new_password
|
404
222
|
set_auth
|
405
223
|
end
|
406
224
|
|
407
|
-
#
|
408
|
-
|
225
|
+
# Invokes the method named +method+ with the parameters given by
|
226
|
+
# +args+ on the XML-RPC server.
|
227
|
+
#
|
228
|
+
# The +method+ parameter is converted into a String and should
|
229
|
+
# be a valid XML-RPC method-name.
|
230
|
+
#
|
231
|
+
# Each parameter of +args+ must be of one of the following types,
|
232
|
+
# where Hash, Struct and Array can contain any of these listed _types_:
|
233
|
+
#
|
234
|
+
# * Fixnum, Bignum
|
235
|
+
# * TrueClass, FalseClass, +true+, +false+
|
236
|
+
# * String, Symbol
|
237
|
+
# * Float
|
238
|
+
# * Hash, Struct
|
239
|
+
# * Array
|
240
|
+
# * Date, Time, XMLRPC::DateTime
|
241
|
+
# * XMLRPC::Base64
|
242
|
+
# * A Ruby object which class includes XMLRPC::Marshallable
|
243
|
+
# (only if Config::ENABLE_MARSHALLABLE is +true+).
|
244
|
+
# That object is converted into a hash, with one additional key/value
|
245
|
+
# pair <code>___class___</code> which contains the class name
|
246
|
+
# for restoring that object later.
|
247
|
+
#
|
248
|
+
# The method returns the return-value from the Remote Procedure Call.
|
249
|
+
#
|
250
|
+
# The type of the return-value is one of the types shown above.
|
251
|
+
#
|
252
|
+
# A Bignum is only allowed when it fits in 32-bit. A XML-RPC
|
253
|
+
# +dateTime.iso8601+ type is always returned as a XMLRPC::DateTime object.
|
254
|
+
# Struct is never returned, only a Hash, the same for a Symbol, where as a
|
255
|
+
# String is always returned. XMLRPC::Base64 is returned as a String from
|
256
|
+
# xmlrpc4r version 1.6.1 on.
|
257
|
+
#
|
258
|
+
# If the remote procedure returned a fault-structure, then a
|
259
|
+
# XMLRPC::FaultException exception is raised, which has two accessor-methods
|
260
|
+
# +faultCode+ an Integer, and +faultString+ a String.
|
409
261
|
def call(method, *args)
|
410
|
-
ok, param = call2(method, *args)
|
262
|
+
ok, param = call2(method, *args)
|
411
263
|
if ok
|
412
264
|
param
|
413
265
|
else
|
414
266
|
raise param
|
415
267
|
end
|
416
|
-
end
|
268
|
+
end
|
417
269
|
|
270
|
+
# The difference between this method and XMLRPC::Client#call is, that
|
271
|
+
# this method will <b>NOT</b> raise a XMLRPC::FaultException exception.
|
272
|
+
#
|
273
|
+
# The method returns an array of two values. The first value indicates if
|
274
|
+
# the second value is +true+ or an XMLRPC::FaultException.
|
275
|
+
#
|
276
|
+
# Both are explained in XMLRPC::Client#call.
|
277
|
+
#
|
278
|
+
# Simple to remember: The "2" in "call2" denotes the number of values it returns.
|
418
279
|
def call2(method, *args)
|
419
280
|
request = create().methodCall(method, *args)
|
420
281
|
data = do_rpc(request, false)
|
421
282
|
parser().parseMethodResponse(data)
|
422
283
|
end
|
423
284
|
|
285
|
+
# Similar to XMLRPC::Client#call, however can be called concurrently and
|
286
|
+
# use a new connection for each request. In contrast to the corresponding
|
287
|
+
# method without the +_async+ suffix, which use connect-alive (one
|
288
|
+
# connection for all requests).
|
289
|
+
#
|
290
|
+
# Note, that you have to use Thread to call these methods concurrently.
|
291
|
+
# The following example calls two methods concurrently:
|
292
|
+
#
|
293
|
+
# Thread.new {
|
294
|
+
# p client.call_async("michael.add", 4, 5)
|
295
|
+
# }
|
296
|
+
#
|
297
|
+
# Thread.new {
|
298
|
+
# p client.call_async("michael.div", 7, 9)
|
299
|
+
# }
|
300
|
+
#
|
424
301
|
def call_async(method, *args)
|
425
|
-
ok, param = call2_async(method, *args)
|
302
|
+
ok, param = call2_async(method, *args)
|
426
303
|
if ok
|
427
304
|
param
|
428
305
|
else
|
429
306
|
raise param
|
430
307
|
end
|
431
|
-
end
|
308
|
+
end
|
432
309
|
|
310
|
+
# Same as XMLRPC::Client#call2, but can be called concurrently.
|
311
|
+
#
|
312
|
+
# See also XMLRPC::Client#call_async
|
433
313
|
def call2_async(method, *args)
|
434
314
|
request = create().methodCall(method, *args)
|
435
315
|
data = do_rpc(request, true)
|
@@ -437,8 +317,14 @@ module XMLRPC
|
|
437
317
|
end
|
438
318
|
|
439
319
|
|
440
|
-
#
|
441
|
-
|
320
|
+
# You can use this method to execute several methods on a XMLRPC server
|
321
|
+
# which support the multi-call extension.
|
322
|
+
#
|
323
|
+
# s.multicall(
|
324
|
+
# ['michael.add', 3, 4],
|
325
|
+
# ['michael.sub', 4, 5]
|
326
|
+
# )
|
327
|
+
# # => [7, -1]
|
442
328
|
def multicall(*methods)
|
443
329
|
ok, params = multicall2(*methods)
|
444
330
|
if ok
|
@@ -448,10 +334,30 @@ module XMLRPC
|
|
448
334
|
end
|
449
335
|
end
|
450
336
|
|
337
|
+
# Same as XMLRPC::Client#multicall, but returns two parameters instead of
|
338
|
+
# raising an XMLRPC::FaultException.
|
339
|
+
#
|
340
|
+
# See XMLRPC::Client#call2
|
451
341
|
def multicall2(*methods)
|
452
342
|
gen_multicall(methods, false)
|
453
343
|
end
|
454
344
|
|
345
|
+
# Similar to XMLRPC::Client#multicall, however can be called concurrently and
|
346
|
+
# use a new connection for each request. In contrast to the corresponding
|
347
|
+
# method without the +_async+ suffix, which use connect-alive (one
|
348
|
+
# connection for all requests).
|
349
|
+
#
|
350
|
+
# Note, that you have to use Thread to call these methods concurrently.
|
351
|
+
# The following example calls two methods concurrently:
|
352
|
+
#
|
353
|
+
# Thread.new {
|
354
|
+
# p client.multicall_async("michael.add", 4, 5)
|
355
|
+
# }
|
356
|
+
#
|
357
|
+
# Thread.new {
|
358
|
+
# p client.multicall_async("michael.div", 7, 9)
|
359
|
+
# }
|
360
|
+
#
|
455
361
|
def multicall_async(*methods)
|
456
362
|
ok, params = multicall2_async(*methods)
|
457
363
|
if ok
|
@@ -461,31 +367,65 @@ module XMLRPC
|
|
461
367
|
end
|
462
368
|
end
|
463
369
|
|
370
|
+
# Same as XMLRPC::Client#multicall2, but can be called concurrently.
|
371
|
+
#
|
372
|
+
# See also XMLRPC::Client#multicall_async
|
464
373
|
def multicall2_async(*methods)
|
465
374
|
gen_multicall(methods, true)
|
466
375
|
end
|
467
376
|
|
468
377
|
|
469
|
-
# Proxy
|
470
|
-
|
378
|
+
# Returns an object of class XMLRPC::Client::Proxy, initialized with
|
379
|
+
# +prefix+ and +args+.
|
380
|
+
#
|
381
|
+
# A proxy object returned by this method behaves like XMLRPC::Client#call,
|
382
|
+
# i.e. a call on that object will raise a XMLRPC::FaultException when a
|
383
|
+
# fault-structure is returned by that call.
|
471
384
|
def proxy(prefix=nil, *args)
|
472
385
|
Proxy.new(self, prefix, args, :call)
|
473
386
|
end
|
474
387
|
|
388
|
+
# Almost the same like XMLRPC::Client#proxy only that a call on the returned
|
389
|
+
# XMLRPC::Client::Proxy object will return two parameters.
|
390
|
+
#
|
391
|
+
# See XMLRPC::Client#call2
|
475
392
|
def proxy2(prefix=nil, *args)
|
476
393
|
Proxy.new(self, prefix, args, :call2)
|
477
394
|
end
|
478
395
|
|
396
|
+
# Similar to XMLRPC::Client#proxy, however can be called concurrently and
|
397
|
+
# use a new connection for each request. In contrast to the corresponding
|
398
|
+
# method without the +_async+ suffix, which use connect-alive (one
|
399
|
+
# connection for all requests).
|
400
|
+
#
|
401
|
+
# Note, that you have to use Thread to call these methods concurrently.
|
402
|
+
# The following example calls two methods concurrently:
|
403
|
+
#
|
404
|
+
# Thread.new {
|
405
|
+
# p client.proxy_async("michael.add", 4, 5)
|
406
|
+
# }
|
407
|
+
#
|
408
|
+
# Thread.new {
|
409
|
+
# p client.proxy_async("michael.div", 7, 9)
|
410
|
+
# }
|
411
|
+
#
|
479
412
|
def proxy_async(prefix=nil, *args)
|
480
413
|
Proxy.new(self, prefix, args, :call_async)
|
481
414
|
end
|
482
415
|
|
416
|
+
# Same as XMLRPC::Client#proxy2, but can be called concurrently.
|
417
|
+
#
|
418
|
+
# See also XMLRPC::Client#proxy_async
|
483
419
|
def proxy2_async(prefix=nil, *args)
|
484
420
|
Proxy.new(self, prefix, args, :call2_async)
|
485
421
|
end
|
486
422
|
|
487
423
|
|
488
|
-
private
|
424
|
+
private
|
425
|
+
|
426
|
+
def net_http(host, port, proxy_host, proxy_port)
|
427
|
+
Net::HTTP.new host, port, proxy_host, proxy_port
|
428
|
+
end
|
489
429
|
|
490
430
|
def set_auth
|
491
431
|
if @user.nil?
|
@@ -493,15 +433,15 @@ module XMLRPC
|
|
493
433
|
else
|
494
434
|
a = "#@user"
|
495
435
|
a << ":#@password" if @password != nil
|
496
|
-
@auth =
|
436
|
+
@auth = "Basic " + [a].pack("m0")
|
497
437
|
end
|
498
438
|
end
|
499
439
|
|
500
440
|
def do_rpc(request, async=false)
|
501
|
-
header = {
|
441
|
+
header = {
|
502
442
|
"User-Agent" => USER_AGENT,
|
503
443
|
"Content-Type" => "text/xml; charset=utf-8",
|
504
|
-
"Content-Length" => request.
|
444
|
+
"Content-Length" => request.bytesize.to_s,
|
505
445
|
"Connection" => (async ? "close" : "keep-alive")
|
506
446
|
}
|
507
447
|
|
@@ -512,44 +452,45 @@ module XMLRPC
|
|
512
452
|
# add authorization header
|
513
453
|
header["Authorization"] = @auth
|
514
454
|
end
|
515
|
-
|
455
|
+
|
516
456
|
resp = nil
|
517
457
|
@http_last_response = nil
|
518
458
|
|
519
459
|
if async
|
520
|
-
# use a new HTTP object for each call
|
521
|
-
|
522
|
-
http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
|
460
|
+
# use a new HTTP object for each call
|
461
|
+
http = net_http(@host, @port, @proxy_host, @proxy_port)
|
523
462
|
http.use_ssl = @use_ssl if @use_ssl
|
524
463
|
http.read_timeout = @timeout
|
525
464
|
http.open_timeout = @timeout
|
526
|
-
|
465
|
+
|
527
466
|
# post request
|
528
467
|
http.start {
|
529
|
-
resp = http.
|
468
|
+
resp = http.request_post(@path, request, header)
|
530
469
|
}
|
531
470
|
else
|
532
471
|
# reuse the HTTP object for each call => connection alive is possible
|
533
472
|
# we must start connection explicitely first time so that http.request
|
534
473
|
# does not assume that we don't want keepalive
|
535
474
|
@http.start if not @http.started?
|
536
|
-
|
475
|
+
|
537
476
|
# post request
|
538
|
-
resp = @http.
|
477
|
+
resp = @http.request_post(@path, request, header)
|
539
478
|
end
|
540
|
-
|
479
|
+
|
541
480
|
@http_last_response = resp
|
542
481
|
|
543
482
|
data = resp.body
|
544
483
|
|
545
484
|
if resp.code == "401"
|
546
485
|
# Authorization Required
|
547
|
-
raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
|
486
|
+
raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
|
548
487
|
elsif resp.code[0,1] != "2"
|
549
|
-
raise "HTTP-Error: #{resp.code} #{resp.message}"
|
488
|
+
raise "HTTP-Error: #{resp.code} #{resp.message}"
|
550
489
|
end
|
551
490
|
|
552
|
-
|
491
|
+
# assume text/xml on instances where Content-Type header is not set
|
492
|
+
ct_expected = resp["Content-Type"] || 'text/xml'
|
493
|
+
ct = parse_content_type(ct_expected).first
|
553
494
|
if ct != "text/xml"
|
554
495
|
if ct == "text/html"
|
555
496
|
raise "Wrong content-type (received '#{ct}' but expected 'text/xml'): \n#{data}"
|
@@ -559,10 +500,10 @@ module XMLRPC
|
|
559
500
|
end
|
560
501
|
|
561
502
|
expected = resp["Content-Length"] || "<unknown>"
|
562
|
-
if data.nil? or data.
|
563
|
-
raise "Wrong size. Was #{data.
|
564
|
-
elsif expected != "<unknown>" and expected.to_i != data.
|
565
|
-
raise "Wrong size. Was #{data.
|
503
|
+
if data.nil? or data.bytesize == 0
|
504
|
+
raise "Wrong size. Was #{data.bytesize}, should be #{expected}"
|
505
|
+
elsif expected != "<unknown>" and expected.to_i != data.bytesize and resp["Transfer-Encoding"].nil?
|
506
|
+
raise "Wrong size. Was #{data.bytesize}, should be #{expected}"
|
566
507
|
end
|
567
508
|
|
568
509
|
set_cookies = resp.get_fields("Set-Cookie")
|
@@ -581,11 +522,11 @@ module XMLRPC
|
|
581
522
|
meth = :call2
|
582
523
|
meth = :call2_async if async
|
583
524
|
|
584
|
-
ok, params = self.send(meth, "system.multicall",
|
525
|
+
ok, params = self.send(meth, "system.multicall",
|
585
526
|
methods.collect {|m| {'methodName' => m[0], 'params' => m[1..-1]} }
|
586
527
|
)
|
587
528
|
|
588
|
-
if ok
|
529
|
+
if ok
|
589
530
|
params = params.collect do |param|
|
590
531
|
if param.is_a? Array
|
591
532
|
param[0]
|
@@ -593,7 +534,7 @@ module XMLRPC
|
|
593
534
|
XMLRPC::FaultException.new(param["faultCode"], param["faultString"])
|
594
535
|
else
|
595
536
|
raise "Wrong multicall return value"
|
596
|
-
end
|
537
|
+
end
|
597
538
|
end
|
598
539
|
end
|
599
540
|
|
@@ -602,19 +543,55 @@ module XMLRPC
|
|
602
543
|
|
603
544
|
|
604
545
|
|
546
|
+
# XML-RPC calls look nicer!
|
547
|
+
#
|
548
|
+
# You can call any method onto objects of that class - the object handles
|
549
|
+
# XMLRPC::Client::Proxy#method_missing and will forward the method call to
|
550
|
+
# a XML-RPC server.
|
551
|
+
#
|
552
|
+
# Don't use this class directly, instead use the public instance method
|
553
|
+
# XMLRPC::Client#proxy or XMLRPC::Client#proxy2.
|
554
|
+
#
|
555
|
+
# require "xmlrpc/client"
|
556
|
+
#
|
557
|
+
# server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
|
558
|
+
#
|
559
|
+
# michael = server.proxy("michael")
|
560
|
+
# michael2 = server.proxy("michael", 4)
|
561
|
+
#
|
562
|
+
# # both calls should return the same value '9'.
|
563
|
+
# p michael.add(4,5)
|
564
|
+
# p michael2.add(5)
|
605
565
|
class Proxy
|
606
566
|
|
567
|
+
# Creates an object which provides XMLRPC::Client::Proxy#method_missing.
|
568
|
+
#
|
569
|
+
# The given +server+ must be an instance of XMLRPC::Client, which is the
|
570
|
+
# XML-RPC server to be used for a XML-RPC call.
|
571
|
+
#
|
572
|
+
# +prefix+ and +delim+ will be prepended to the method name called onto this object.
|
573
|
+
#
|
574
|
+
# An optional parameter +meth+ is the method to use for a RPC.
|
575
|
+
# It can be either, call, call2, call_async, call2_async
|
576
|
+
#
|
577
|
+
# +args+ are arguments which are automatically given to every XML-RPC
|
578
|
+
# call before being provided through +method_missing+.
|
607
579
|
def initialize(server, prefix, args=[], meth=:call, delim=".")
|
608
|
-
|
609
|
-
|
610
|
-
|
580
|
+
@server = server
|
581
|
+
@prefix = prefix ? prefix + delim : ""
|
582
|
+
@args = args
|
611
583
|
@meth = meth
|
612
584
|
end
|
613
585
|
|
586
|
+
# Every method call is forwarded to the XML-RPC server defined in
|
587
|
+
# XMLRPC::Client::Proxy#new.
|
588
|
+
#
|
589
|
+
# Note: Inherited methods from class Object cannot be used as XML-RPC
|
590
|
+
# names, because they get around +method_missing+.
|
614
591
|
def method_missing(mid, *args)
|
615
|
-
|
616
|
-
|
617
|
-
|
592
|
+
pre = @prefix + mid.to_s
|
593
|
+
arg = @args + args
|
594
|
+
@server.send(@meth, pre, *arg)
|
618
595
|
end
|
619
596
|
|
620
597
|
end # class Proxy
|