mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
added howto
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@7287 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
597e0c74ba
commit
f6c1872354
2 changed files with 301 additions and 0 deletions
1
lib/xmlrpc/.document
Normal file
1
lib/xmlrpc/.document
Normal file
|
@ -0,0 +1 @@
|
|||
README.rdoc
|
300
lib/xmlrpc/README.rdoc
Normal file
300
lib/xmlrpc/README.rdoc
Normal file
|
@ -0,0 +1,300 @@
|
|||
= XMLRPC for Ruby
|
||||
|
||||
== 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/projects/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 Authentification
|
||||
* HTTPS protocol (SSL)
|
||||
|
||||
* Parsers
|
||||
* NQXML (NQXMLStreamParser, NQXMLTreeParser)
|
||||
* Expat (XMLStreamParser, XMLTreeParser)
|
||||
* REXML (REXMLStreamParser)
|
||||
* xml-scan (XMLScanStreamParser)
|
||||
* Fastest parser is Expat's 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 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}"
|
||||
|
||||
=== 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
|
||||
|
||||
=== Client using 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
|
||||
<i>proxy2</i>. 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
|
||||
|
||||
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 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::iPIMethods})) 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 server
|
||||
|
||||
Same as CGI-based server, only that the line
|
||||
|
||||
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 XML Parser or XML Writer
|
||||
|
||||
The examples above all use the default parser (which is now since 1.8
|
||||
REXMLStreamParser) and a default XML writer. If you want to use a different
|
||||
XML parser, then you have to call the <i>set_parser</i> method of
|
||||
<tt>XMLRPC::Client</tt> instances or instances of subclasses of
|
||||
<tt>XMLRPC::BasicServer</tt> or by editing xmlrpc/config.rb.
|
||||
|
||||
Client Example:
|
||||
|
||||
# ...
|
||||
server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php")
|
||||
server.set_parser(XMLRPC::XMLParser::XMLParser.new)
|
||||
# ...
|
||||
|
||||
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 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, XMLStreamParser is ~350 (!) times faster than
|
||||
NQXMLTreeParser and still ~18 times as fast as XMLTreeParser.
|
||||
|
||||
You can change the XML-writer by calling method <i>set_writer</i>.
|
Loading…
Reference in a new issue