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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/rubysl/xmlrpc.rb +1 -0
- data/lib/rubysl/xmlrpc/version.rb +5 -0
- data/lib/xmlrpc/README.txt +31 -0
- data/lib/xmlrpc/base64.rb +81 -0
- data/lib/xmlrpc/client.rb +625 -0
- data/lib/xmlrpc/config.rb +40 -0
- data/lib/xmlrpc/create.rb +290 -0
- data/lib/xmlrpc/datetime.rb +142 -0
- data/lib/xmlrpc/httpserver.rb +178 -0
- data/lib/xmlrpc/marshal.rb +76 -0
- data/lib/xmlrpc/parser.rb +813 -0
- data/lib/xmlrpc/server.rb +782 -0
- data/lib/xmlrpc/utils.rb +165 -0
- data/rubysl-xmlrpc.gemspec +23 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1 @@
|
|
1
|
+
require "rubysl/xmlrpc/version"
|
@@ -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
|
+
|