summaryrefslogtreecommitdiff
path: root/lib/xmlrpc.rb
blob: 511c57d2695c1530e918dea747c8a47c365ac35e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
# == Author and Copyright
#
# Copyright (C) 2001-2004 by Michael Neumann (mailto:mneumann@ntecs.de)
#
# Released under the same term of license as Ruby.
#
# == Overview
#
# XMLRPC is a lightweight protocol that enables remote procedure calls over
# HTTP.  It is defined at http://www.xmlrpc.com.
#
# XMLRPC allows you to create simple distributed computing solutions that span
# computer languages.  Its distinctive feature is its simplicity compared to
# other approaches like SOAP and CORBA.
#
# The Ruby standard library package 'xmlrpc' enables you to create a server that
# implements remote procedures and a client that calls them.  Very little code
# is required to achieve either of these.
#
# == Example
#
# Try the following code.  It calls a standard demonstration remote procedure.
#
#   require 'xmlrpc/client'
#   require 'pp'
#
#   server = XMLRPC::Client.new2("http://xmlrpc-c.sourceforge.net/api/sample.php")
#   result = server.call("sample.sumAndDifference", 5, 3)
#   pp result
#
# == Documentation
#
# See http://www.ntecs.de/ruby/xmlrpc4r/.  There is plenty of detail there to
# use the client and implement a server.
#
# == Features of XMLRPC for Ruby
#
# * Extensions
#   * Introspection
#   * multiCall
#   * optionally nil values and integers larger than 32 Bit
#
# * Server
#   * Standalone XML-RPC server
#   * CGI-based (works with FastCGI)
#   * Apache mod_ruby server
#   * WEBrick servlet
#
# * Client
#   * synchronous/asynchronous calls
#   * Basic HTTP-401 Authentication
#   * HTTPS protocol (SSL)
#
# * Parsers
#   * NQXML (XMLParser::NQXMLStreamParser, XMLParser::NQXMLTreeParser)
#   * Expat (XMLParser::XMLStreamParser, XMLParser::XMLTreeParser)
#   * REXML (XMLParser::REXMLStreamParser)
#   * xml-scan (XMLParser::XMLScanStreamParser)
#   * Fastest parser is Expat's XMLParser::XMLStreamParser!
#
# * General
#   * possible to choose between XMLParser module (Expat wrapper) and REXML/NQXML (pure Ruby) parsers
#   * Marshalling Ruby objects to Hashs and reconstruct them later from a Hash
#   * SandStorm component architecture XMLRPC::Client interface
#
# == Howto
#
# === Client
#
#   require "xmlrpc/client"
#
#   # Make an object to represent the XML-RPC server.
#   server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
#
#   # Call the remote server and get our result
#   result = server.call("sample.sumAndDifference", 5, 3)
#
#   sum = result["sum"]
#   difference = result["difference"]
#
#   puts "Sum: #{sum}, Difference: #{difference}"
#
# === XMLRPC::Client with XML-RPC fault-structure handling
#
# There are two possible ways, of handling a fault-structure:
#
# ==== by catching a XMLRPC::FaultException exception
#
#   require "xmlrpc/client"
#
#   # Make an object to represent the XML-RPC server.
#   server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
#
#   begin
#     # Call the remote server and get our result
#     result = server.call("sample.sumAndDifference", 5, 3)
#
#     sum = result["sum"]
#     difference = result["difference"]
#
#     puts "Sum: #{sum}, Difference: #{difference}"
#
#   rescue XMLRPC::FaultException => e
#     puts "Error: "
#     puts e.faultCode
#     puts e.faultString
#   end
#
# ==== by calling "call2" which returns a boolean
#
#   require "xmlrpc/client"
#
#   # Make an object to represent the XML-RPC server.
#   server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
#
#   # Call the remote server and get our result
#   ok, result = server.call2("sample.sumAndDifference", 5, 3)
#
#   if ok
#     sum = result["sum"]
#     difference = result["difference"]
#
#     puts "Sum: #{sum}, Difference: #{difference}"
#   else
#     puts "Error: "
#     puts result.faultCode
#     puts result.faultString
#   end
#
# === Using XMLRPC::Client::Proxy
#
# You can create a Proxy object onto which you can call methods. This way it
# looks nicer. Both forms, _call_ and _call2_ are supported through _proxy_ and
# _proxy2_.  You can additionally give arguments to the Proxy, which will be
# given to each XML-RPC call using that Proxy.
#
#   require "xmlrpc/client"
#
#   # Make an object to represent the XML-RPC server.
#   server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
#
#   # Create a Proxy object
#   sample = server.proxy("sample")
#
#   # Call the remote server and get our result
#   result = sample.sumAndDifference(5,3)
#
#   sum = result["sum"]
#   difference = result["difference"]
#
#   puts "Sum: #{sum}, Difference: #{difference}"
#
# === CGI-based server using XMLRPC::CGIServer
#
# There are also two ways to define handler, the first is
# like C/PHP, the second like Java, of course both ways
# can be mixed:
#
# ==== C/PHP-like (handler functions)
#
#   require "xmlrpc/server"
#
#   s = XMLRPC::CGIServer.new
#
#   s.add_handler("sample.sumAndDifference") do |a,b|
#     { "sum" => a + b, "difference" => a - b }
#   end
#
#   s.serve
#
# ==== Java-like (handler classes)
#
#   require "xmlrpc/server"
#
#   s = XMLRPC::CGIServer.new
#
#   class MyHandler
#     def sumAndDifference(a, b)
#       { "sum" => a + b, "difference" => a - b }
#     end
#   end
#
#   # NOTE: Security Hole (read below)!!!
#   s.add_handler("sample", MyHandler.new)
#   s.serve
#
#
# To return a fault-structure you have to raise an XMLRPC::FaultException e.g.:
#
#   raise XMLRPC::FaultException.new(3, "division by Zero")
#
# ===== Security Note
#
# From Brian Candler:
#
#   Above code sample has an extremely nasty security hole, in that you can now call
#   any method of 'MyHandler' remotely, including methods inherited from Object
#   and Kernel! For example, in the client code, you can use
#
#     puts server.call("sample.send","`","ls")
#
#   (backtick being the method name for running system processes). Needless to
#   say, 'ls' can be replaced with something else.
#
#   The version which binds proc objects (or the version presented below in the next section)
#   doesn't have this problem, but people may be tempted to use the second version because it's
#   so nice and 'Rubyesque'. I think it needs a big red disclaimer.
#
#
# From Michael:
#
# A solution is to undef insecure methods or to use
# XMLRPC::Service::PublicInstanceMethodsInterface as shown below:
#
#   class MyHandler
#     def sumAndDifference(a, b)
#       { "sum" => a + b, "difference" => a - b }
#     end
#   end
#
#   # ... server initialization ...
#
#   s.add_handler(XMLRPC::iPIMethods("sample"), MyHandler.new)
#
#   # ...
#
# This adds only public instance methods explicitly declared in class MyHandler
# (and not those inherited from any other class).
#
# ==== With interface declarations
#
# Code sample from the book Ruby Developer's Guide:
#
#   require "xmlrpc/server"
#
#   class Num
#     INTERFACE = XMLRPC::interface("num") {
#       meth 'int add(int, int)', 'Add two numbers', 'add'
#       meth 'int div(int, int)', 'Divide two numbers'
#     }
#
#     def add(a, b) a + b end
#     def div(a, b) a / b end
#   end
#
#
#   s = XMLRPC::CGIServer.new
#   s.add_handler(Num::INTERFACE, Num.new)
#   s.serve
#
# === Standalone XMLRPC::Server
#
# Same as CGI-based server, the only difference being
#
#   server = XMLRPC::CGIServer.new
#
# must be changed to
#
#   server = XMLRPC::Server.new(8080)
#
# if you want a server listening on port 8080.
# The rest is the same.
#
# === Choosing a different XMLParser or XMLWriter
#
# The examples above all use the default parser (which is now since 1.8
# XMLParser::REXMLStreamParser) and a default XMLRPC::XMLWriter.
# If you want to use a different XMLParser, then you have to call the
# ParserWriterChooseMixin#set_parser method of XMLRPC::Client instances
# or instances of subclasses of XMLRPC::BasicServer or by editing
# xmlrpc/config.rb.
#
# XMLRPC::Client Example:
#
#   # ...
#   server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
#   server.set_parser(XMLRPC::XMLParser::XMLParser.new)
#   # ...
#
# XMLRPC::Server Example:
#
#   # ...
#   s = XMLRPC::CGIServer.new
#   s.set_parser(XMLRPC::XMLParser::XMLStreamParser.new)
#   # ...
#
# or:
#
#   # ...
#   server = XMLRPC::Server.new(8080)
#   server.set_parser(XMLRPC::XMLParser::NQXMLParser.new)
#   # ...
#
#
# Note that XMLParser::XMLStreamParser is incredible faster (and uses less memory) than any
# other parser and scales well for large documents. For example for a 0.5 MB XML
# document with many tags, XMLParser::XMLStreamParser is ~350 (!) times faster than
# XMLParser::NQXMLTreeParser and still ~18 times as fast as XMLParser::XMLTreeParser.
#
# You can change the XML-writer by calling method ParserWriterChooseMixin#set_writer.
module XMLRPC; end