rubysl-xmlrpc 1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 653c1c59479802aa05fedc69c2772ddc62a266b3
4
+ data.tar.gz: e2e58a4eca916a47a94808e2534f5fde00983add
5
+ SHA512:
6
+ metadata.gz: b388332ed3983173bac340d925f8fef701b6fe3a5c2178fb3f16267a2ae4fefacb396456b320182fd6cd84c7adee456ded7435c539c8a00fb0622c2ee5bdf4d0
7
+ data.tar.gz: 762eb72a26c704014dbfc1280c9f7db29b99d0988146b733f57bae95194914ac6a882b662940c3af23edcc9ef67d869e87cf7c770c7351302ca559061071748f
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-xmlrpc.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,29 @@
1
+ # Rubysl::Xmlrpc
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-xmlrpc'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-xmlrpc
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1 @@
1
+ require "rubysl/xmlrpc/version"
@@ -0,0 +1,5 @@
1
+ module RubySL
2
+ module XMLRPC
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,31 @@
1
+ = XMLRPC for Ruby, Standard Library Documentation
2
+
3
+ == Overview
4
+
5
+ XMLRPC is a lightweight protocol that enables remote procedure calls over
6
+ HTTP. It is defined at http://www.xmlrpc.com.
7
+
8
+ XMLRPC allows you to create simple distributed computing solutions that span
9
+ computer languages. Its distinctive feature is its simplicity compared to
10
+ other approaches like SOAP and CORBA.
11
+
12
+ The Ruby standard library package 'xmlrpc' enables you to create a server that
13
+ implements remote procedures and a client that calls them. Very little code
14
+ is required to achieve either of these.
15
+
16
+ == Example
17
+
18
+ Try the following code. It calls a standard demonstration remote procedure.
19
+
20
+ require 'xmlrpc/client'
21
+ require 'pp'
22
+
23
+ server = XMLRPC::Client.new2("http://xmlrpc-c.sourceforge.net/api/sample.php")
24
+ result = server.call("sample.sumAndDifference", 5, 3)
25
+ pp result
26
+
27
+ == Documentation
28
+
29
+ See http://www.ntecs.de/projects/xmlrpc4r. There is plenty of detail there to
30
+ use the client and implement a server.
31
+
@@ -0,0 +1,81 @@
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
+
42
+ class Base64
43
+
44
+ def initialize(str, state = :dec)
45
+ case state
46
+ when :enc
47
+ @str = Base64.decode(str)
48
+ when :dec
49
+ @str = str
50
+ else
51
+ raise ArgumentError, "wrong argument; either :enc or :dec"
52
+ end
53
+ end
54
+
55
+ def decoded
56
+ @str
57
+ end
58
+
59
+ def encoded
60
+ Base64.encode(@str)
61
+ end
62
+
63
+
64
+ def Base64.decode(str)
65
+ str.gsub(/\s+/, "").unpack("m")[0]
66
+ end
67
+
68
+ def Base64.encode(str)
69
+ [str].pack("m")
70
+ end
71
+
72
+ end
73
+
74
+
75
+ end # module XMLRPC
76
+
77
+
78
+ =begin
79
+ = History
80
+ $Id: base64.rb 11708 2007-02-12 23:01:19Z shyouhei $
81
+ =end
@@ -0,0 +1,625 @@
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
+
277
+ require "xmlrpc/parser"
278
+ require "xmlrpc/create"
279
+ require "xmlrpc/config"
280
+ require "xmlrpc/utils" # ParserWriterChooseMixin
281
+ require "net/http"
282
+
283
+ module XMLRPC
284
+
285
+ class Client
286
+
287
+ USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})"
288
+
289
+ include ParserWriterChooseMixin
290
+ include ParseContentType
291
+
292
+
293
+ # Constructors -------------------------------------------------------------------
294
+
295
+ def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
296
+ user=nil, password=nil, use_ssl=nil, timeout=nil)
297
+
298
+ @http_header_extra = nil
299
+ @http_last_response = nil
300
+ @cookie = nil
301
+
302
+ @host = host || "localhost"
303
+ @path = path || "/RPC2"
304
+ @proxy_host = proxy_host
305
+ @proxy_port = proxy_port
306
+ @proxy_host ||= 'localhost' if @proxy_port != nil
307
+ @proxy_port ||= 8080 if @proxy_host != nil
308
+ @use_ssl = use_ssl || false
309
+ @timeout = timeout || 30
310
+
311
+ if use_ssl
312
+ require "net/https"
313
+ @port = port || 443
314
+ else
315
+ @port = port || 80
316
+ end
317
+
318
+ @user, @password = user, password
319
+
320
+ set_auth
321
+
322
+ # convert ports to integers
323
+ @port = @port.to_i if @port != nil
324
+ @proxy_port = @proxy_port.to_i if @proxy_port != nil
325
+
326
+ # HTTP object for synchronous calls
327
+ Net::HTTP.version_1_2
328
+ @http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
329
+ @http.use_ssl = @use_ssl if @use_ssl
330
+ @http.read_timeout = @timeout
331
+ @http.open_timeout = @timeout
332
+
333
+ @parser = nil
334
+ @create = nil
335
+ end
336
+
337
+
338
+ class << self
339
+
340
+ 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
350
+
351
+ else
352
+ raise "Wrong URI as parameter!"
353
+ end
354
+
355
+ proxy_host, proxy_port = (proxy || "").split(":")
356
+
357
+ self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout)
358
+ end
359
+
360
+ alias new_from_uri new2
361
+
362
+ def new3(hash={})
363
+
364
+ # convert all keys into lowercase strings
365
+ h = {}
366
+ hash.each { |k,v| h[k.to_s.downcase] = v }
367
+
368
+ self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'],
369
+ h['use_ssl'], h['timeout'])
370
+ end
371
+
372
+ alias new_from_hash new3
373
+
374
+ end
375
+
376
+
377
+ # Attribute Accessors -------------------------------------------------------------------
378
+
379
+ # add additional HTTP headers to the request
380
+ attr_accessor :http_header_extra
381
+
382
+ # makes last HTTP response accessible
383
+ attr_reader :http_last_response
384
+
385
+ # Cookie support
386
+ attr_accessor :cookie
387
+
388
+
389
+ attr_reader :timeout, :user, :password
390
+
391
+ def timeout=(new_timeout)
392
+ @timeout = new_timeout
393
+ @http.read_timeout = @timeout
394
+ @http.open_timeout = @timeout
395
+ end
396
+
397
+ def user=(new_user)
398
+ @user = new_user
399
+ set_auth
400
+ end
401
+
402
+ def password=(new_password)
403
+ @password = new_password
404
+ set_auth
405
+ end
406
+
407
+ # Call methods --------------------------------------------------------------
408
+
409
+ def call(method, *args)
410
+ ok, param = call2(method, *args)
411
+ if ok
412
+ param
413
+ else
414
+ raise param
415
+ end
416
+ end
417
+
418
+ def call2(method, *args)
419
+ request = create().methodCall(method, *args)
420
+ data = do_rpc(request, false)
421
+ parser().parseMethodResponse(data)
422
+ end
423
+
424
+ def call_async(method, *args)
425
+ ok, param = call2_async(method, *args)
426
+ if ok
427
+ param
428
+ else
429
+ raise param
430
+ end
431
+ end
432
+
433
+ def call2_async(method, *args)
434
+ request = create().methodCall(method, *args)
435
+ data = do_rpc(request, true)
436
+ parser().parseMethodResponse(data)
437
+ end
438
+
439
+
440
+ # Multicall methods --------------------------------------------------------------
441
+
442
+ def multicall(*methods)
443
+ ok, params = multicall2(*methods)
444
+ if ok
445
+ params
446
+ else
447
+ raise params
448
+ end
449
+ end
450
+
451
+ def multicall2(*methods)
452
+ gen_multicall(methods, false)
453
+ end
454
+
455
+ def multicall_async(*methods)
456
+ ok, params = multicall2_async(*methods)
457
+ if ok
458
+ params
459
+ else
460
+ raise params
461
+ end
462
+ end
463
+
464
+ def multicall2_async(*methods)
465
+ gen_multicall(methods, true)
466
+ end
467
+
468
+
469
+ # Proxy generating methods ------------------------------------------
470
+
471
+ def proxy(prefix=nil, *args)
472
+ Proxy.new(self, prefix, args, :call)
473
+ end
474
+
475
+ def proxy2(prefix=nil, *args)
476
+ Proxy.new(self, prefix, args, :call2)
477
+ end
478
+
479
+ def proxy_async(prefix=nil, *args)
480
+ Proxy.new(self, prefix, args, :call_async)
481
+ end
482
+
483
+ def proxy2_async(prefix=nil, *args)
484
+ Proxy.new(self, prefix, args, :call2_async)
485
+ end
486
+
487
+
488
+ private # ----------------------------------------------------------
489
+
490
+ def set_auth
491
+ if @user.nil?
492
+ @auth = nil
493
+ else
494
+ a = "#@user"
495
+ a << ":#@password" if @password != nil
496
+ @auth = ("Basic " + [a].pack("m")).chomp
497
+ end
498
+ end
499
+
500
+ def do_rpc(request, async=false)
501
+ header = {
502
+ "User-Agent" => USER_AGENT,
503
+ "Content-Type" => "text/xml; charset=utf-8",
504
+ "Content-Length" => request.size.to_s,
505
+ "Connection" => (async ? "close" : "keep-alive")
506
+ }
507
+
508
+ header["Cookie"] = @cookie if @cookie
509
+ header.update(@http_header_extra) if @http_header_extra
510
+
511
+ if @auth != nil
512
+ # add authorization header
513
+ header["Authorization"] = @auth
514
+ end
515
+
516
+ resp = nil
517
+ @http_last_response = nil
518
+
519
+ 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)
523
+ http.use_ssl = @use_ssl if @use_ssl
524
+ http.read_timeout = @timeout
525
+ http.open_timeout = @timeout
526
+
527
+ # post request
528
+ http.start {
529
+ resp = http.post2(@path, request, header)
530
+ }
531
+ else
532
+ # reuse the HTTP object for each call => connection alive is possible
533
+ # we must start connection explicitely first time so that http.request
534
+ # does not assume that we don't want keepalive
535
+ @http.start if not @http.started?
536
+
537
+ # post request
538
+ resp = @http.post2(@path, request, header)
539
+ end
540
+
541
+ @http_last_response = resp
542
+
543
+ data = resp.body
544
+
545
+ if resp.code == "401"
546
+ # Authorization Required
547
+ raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}"
548
+ elsif resp.code[0,1] != "2"
549
+ raise "HTTP-Error: #{resp.code} #{resp.message}"
550
+ end
551
+
552
+ ct = parse_content_type(resp["Content-Type"]).first
553
+ if ct != "text/xml"
554
+ if ct == "text/html"
555
+ raise "Wrong content-type (received '#{ct}' but expected 'text/xml'): \n#{data}"
556
+ else
557
+ raise "Wrong content-type (received '#{ct}' but expected 'text/xml')"
558
+ end
559
+ end
560
+
561
+ 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}"
566
+ end
567
+
568
+ set_cookies = resp.get_fields("Set-Cookie")
569
+ if set_cookies and !set_cookies.empty?
570
+ require 'webrick/cookie'
571
+ @cookie = set_cookies.collect do |set_cookie|
572
+ cookie = WEBrick::Cookie.parse_set_cookie(set_cookie)
573
+ WEBrick::Cookie.new(cookie.name, cookie.value).to_s
574
+ end.join("; ")
575
+ end
576
+
577
+ return data
578
+ end
579
+
580
+ def gen_multicall(methods=[], async=false)
581
+ meth = :call2
582
+ meth = :call2_async if async
583
+
584
+ ok, params = self.send(meth, "system.multicall",
585
+ methods.collect {|m| {'methodName' => m[0], 'params' => m[1..-1]} }
586
+ )
587
+
588
+ if ok
589
+ params = params.collect do |param|
590
+ if param.is_a? Array
591
+ param[0]
592
+ elsif param.is_a? Hash
593
+ XMLRPC::FaultException.new(param["faultCode"], param["faultString"])
594
+ else
595
+ raise "Wrong multicall return value"
596
+ end
597
+ end
598
+ end
599
+
600
+ return ok, params
601
+ end
602
+
603
+
604
+
605
+ class Proxy
606
+
607
+ def initialize(server, prefix, args=[], meth=:call, delim=".")
608
+ @server = server
609
+ @prefix = prefix ? prefix + delim : ""
610
+ @args = args
611
+ @meth = meth
612
+ end
613
+
614
+ def method_missing(mid, *args)
615
+ pre = @prefix + mid.to_s
616
+ arg = @args + args
617
+ @server.send(@meth, pre, *arg)
618
+ end
619
+
620
+ end # class Proxy
621
+
622
+ end # class Client
623
+
624
+ end # module XMLRPC
625
+