rubysl-xmlrpc 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,46 +1,23 @@
1
- =begin
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
- = Classes
8
- * ((<XMLRPC::Base64>))
9
-
10
- = XMLRPC::Base64
11
- == Description
12
- This class is necessary for (('xmlrpc4r')) to determine that a string should
13
- be transmitted base64-encoded and not as a raw-string.
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: base64.rb 11708 2007-02-12 23:01:19Z shyouhei $
61
+ $Id$
81
62
  =end
@@ -1,302 +1,90 @@
1
- =begin
2
- = xmlrpc/client.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
- = Classes
8
- * ((<XMLRPC::Client>))
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
- module XMLRPC
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
- # Constructors -------------------------------------------------------------------
294
-
295
- def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
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
- Net::HTTP.version_1_2
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
- if match = /^([^:]+):\/\/(([^@]+)@)?([^\/]+)(\/.*)?$/.match(uri)
342
- proto = match[1]
343
- user, passwd = (match[3] || "").split(":")
344
- host, port = match[4].split(":")
345
- path = match[5]
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
- else
352
- raise "Wrong URI as parameter!"
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
- # Attribute Accessors -------------------------------------------------------------------
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
- # makes last HTTP response accessible
194
+ # Returns the Net::HTTPResponse object of the last RPC.
383
195
  attr_reader :http_last_response
384
196
 
385
- # Cookie support
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
- # Call methods --------------------------------------------------------------
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
- # Multicall methods --------------------------------------------------------------
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 generating methods ------------------------------------------
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 = ("Basic " + [a].pack("m")).chomp
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.size.to_s,
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
- Net::HTTP.version_1_2
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.post2(@path, request, header)
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.post2(@path, request, header)
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
- ct = parse_content_type(resp["Content-Type"]).first
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.size == 0
563
- raise "Wrong size. Was #{data.size}, should be #{expected}"
564
- elsif expected != "<unknown>" and expected.to_i != data.size and resp["Transfer-Encoding"].nil?
565
- raise "Wrong size. Was #{data.size}, should be #{expected}"
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
- @server = server
609
- @prefix = prefix ? prefix + delim : ""
610
- @args = args
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
- pre = @prefix + mid.to_s
616
- arg = @args + args
617
- @server.send(@meth, pre, *arg)
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