Project

General

Profile

Bug #6909 » 0001-Documentation-for-XMLRPC.patch

zzak (zzak _), 08/23/2012 09:22 AM

View differences:

lib/xmlrpc.rb
# == 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 (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
lib/xmlrpc/README.rdoc
= 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>.
lib/xmlrpc/README.txt
= XMLRPC for Ruby, Standard Library Documentation
== 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.
lib/xmlrpc/base64.rb
=begin
= xmlrpc/base64.rb
Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
Released under the same term of license as Ruby.
= Classes
* ((<XMLRPC::Base64>))
= XMLRPC::Base64
== Description
This class is necessary for (('xmlrpc4r')) to determine that a string should
be transmitted base64-encoded and not as a raw-string.
You can use (({XMLRPC::Base64})) on the client and server-side as a
parameter and/or return-value.
== Class Methods
--- XMLRPC::Base64.new( str, state = :dec )
Creates a new (({XMLRPC::Base64})) instance with string ((|str|)) as the
internal string. When ((|state|)) is (({:dec})) it assumes that the
string ((|str|)) is not in base64 format (perhaps already decoded),
otherwise if ((|state|)) is (({:enc})) it decodes ((|str|))
and stores it as the internal string.
--- XMLRPC::Base64.decode( str )
Decodes string ((|str|)) with base64 and returns that value.
--- XMLRPC::Base64.encode( str )
Encodes string ((|str|)) with base64 and returns that value.
== Instance Methods
--- XMLRPC::Base64#decoded
Returns the internal string decoded.
--- XMLRPC::Base64#encoded
Returns the internal string encoded with base64.
=end
module XMLRPC
class Base64
def initialize(str, state = :dec)
case state
when :enc
@str = Base64.decode(str)
when :dec
@str = str
else
raise ArgumentError, "wrong argument; either :enc or :dec"
#
# xmlrpc/base64.rb
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
#
# Released under the same term of license as Ruby.
module XMLRPC # :nodoc:
# This class is necessary for 'xmlrpc4r' to determine that a string should
# be transmitted base64-encoded and not as a raw-string.
#
# You can use XMLRPC::Base64 on the client and server-side as a
# parameter and/or return-value.
class Base64
# Creates a new XMLRPC::Base64 instance with string +str+ as the
# internal string. When +state+ is +:dec+ it assumes that the
# string +str+ is not in base64 format (perhaps already decoded),
# otherwise if +state+ is +:enc+ it decodes +str+
# and stores it as the internal string.
def initialize(str, state = :dec)
case state
when :enc
@str = Base64.decode(str)
when :dec
@str = str
else
raise ArgumentError, "wrong argument; either :enc or :dec"
end
end
end
def decoded
@str
end
# Returns the decoded internal string.
def decoded
@str
end
def encoded
Base64.encode(@str)
end
# Returns the base64 encoded internal string.
def encoded
Base64.encode(@str)
end
# Decodes string +str+ with base64 and returns that value.
def Base64.decode(str)
str.gsub(/\s+/, "").unpack("m")[0]
end
def Base64.decode(str)
str.gsub(/\s+/, "").unpack("m")[0]
end
# Encodes string +str+ with base64 and returns that value.
def Base64.encode(str)
[str].pack("m")
end
def Base64.encode(str)
[str].pack("m")
end
end
end # module XMLRPC
=begin
= History
$Id$
lib/xmlrpc/client.rb
=begin
= xmlrpc/client.rb
Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
Released under the same term of license as Ruby.
= Classes
* ((<XMLRPC::Client>))
* ((<XMLRPC::Client::Proxy>))
= XMLRPC::Client
== Synopsis
require "xmlrpc/client"
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
begin
param = server.call("michael.add", 4, 5)
puts "4 + 5 = #{param}"
rescue XMLRPC::FaultException => e
puts "Error:"
puts e.faultCode
puts e.faultString
end
or
require "xmlrpc/client"
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
ok, param = server.call2("michael.add", 4, 5)
if ok then
puts "4 + 5 = #{param}"
else
puts "Error:"
puts param.faultCode
puts param.faultString
end
== Description
Class (({XMLRPC::Client})) provides remote procedure calls to a XML-RPC server.
After setting the connection-parameters with ((<XMLRPC::Client.new>)) which
creates a new (({XMLRPC::Client})) instance, you can execute a remote procedure
by sending the ((<call|XMLRPC::Client#call>)) or ((<call2|XMLRPC::Client#call2>))
message to this new instance. The given parameters indicate which method to
call on the remote-side and of course the parameters for the remote procedure.
== Class Methods
--- XMLRPC::Client.new( host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=false, timeout =nil)
Creates an object which represents the remote XML-RPC server on the
given host ((|host|)). If the server is CGI-based, ((|path|)) is the
path to the CGI-script, which will be called, otherwise (in the
case of a standalone server) ((|path|)) should be (({"/RPC2"})).
((|port|)) is the port on which the XML-RPC server listens.
If ((|proxy_host|)) is given, then a proxy server listening at
((|proxy_host|)) is used. ((|proxy_port|)) is the port of the
proxy server.
Default values for ((|host|)), ((|path|)) and ((|port|)) are 'localhost', '/RPC2' and
'80' respectively using SSL '443'.
If ((|user|)) and ((|password|)) are given, each time a request is send,
a Authorization header is send. Currently only Basic Authentification is
implemented no Digest.
If ((|use_ssl|)) is set to (({true})), comunication over SSL is enabled.
Note, that you need the SSL package from RAA installed.
Parameter ((|timeout|)) is the time to wait for a XML-RPC response, defaults to 30.
--- XMLRPC::Client.new2( uri, proxy=nil, timeout=nil)
--- XMLRPC::Client.new_from_uri( uri, proxy=nil, timeout=nil)
: uri
URI specifying protocol (http or https), host, port, path, user and password.
Example: https://user:password@host:port/path
: proxy
Is of the form "host:port".
: timeout
Defaults to 30.
--- XMLRPC::Client.new3( hash={} )
--- XMLRPC::Client.new_from_hash( hash={} )
Parameter ((|hash|)) has following case-insensitive keys:
* host
* path
* port
* proxy_host
* proxy_port
* user
* password
* use_ssl
* timeout
Calls ((<XMLRPC::Client.new>)) with the corresponding values.
== Instance Methods
--- XMLRPC::Client#call( method, *args )
Invokes the method named ((|method|)) with the parameters given by
((|args|)) on the XML-RPC server.
The parameter ((|method|)) is converted into a (({String})) and should
be a valid XML-RPC method-name.
Each parameter of ((|args|)) must be of one of the following types,
where (({Hash})), (({Struct})) and (({Array})) can contain any of these listed ((:types:)):
* (({Fixnum})), (({Bignum}))
* (({TrueClass})), (({FalseClass})) ((({true})), (({false})))
* (({String})), (({Symbol}))
* (({Float}))
* (({Hash})), (({Struct}))
* (({Array}))
* (({Date})), (({Time})), (({XMLRPC::DateTime}))
* (({XMLRPC::Base64}))
* A Ruby object which class includes XMLRPC::Marshallable (only if Config::ENABLE_MARSHALLABLE is (({true}))).
That object is converted into a hash, with one additional key/value pair "___class___" which contains the class name
for restoring later that object.
The method returns the return-value from the RPC
((-stands for Remote Procedure Call-)).
The type of the return-value is one of the above shown,
only that a (({Bignum})) is only allowed when it fits in 32-bit and
that a XML-RPC (('dateTime.iso8601')) type is always returned as
a ((<(({XMLRPC::DateTime}))|URL:datetime.html>)) object and
a (({Struct})) is never returned, only a (({Hash})), the same for a (({Symbol})), where
always a (({String})) is returned.
A (({XMLRPC::Base64})) is returned as a (({String})) from xmlrpc4r version 1.6.1 on.
If the remote procedure returned a fault-structure, then a
(({XMLRPC::FaultException})) exception is raised, which has two accessor-methods
(({faultCode})) and (({faultString})) of type (({Integer})) and (({String})).
--- XMLRPC::Client#call2( method, *args )
The difference between this method and ((<call|XMLRPC::Client#call>)) is, that
this method do ((*not*)) raise a (({XMLRPC::FaultException})) exception.
The method returns an array of two values. The first value indicates if
the second value is a return-value ((({true}))) or an object of type
(({XMLRPC::FaultException})).
Both are explained in ((<call|XMLRPC::Client#call>)).
Simple to remember: The "2" in "call2" denotes the number of values it returns.
--- XMLRPC::Client#multicall( *methods )
You can use this method to execute several methods on a XMLRPC server which supports
the multi-call extension.
Example:
s.multicall(
['michael.add', 3, 4],
['michael.sub', 4, 5]
)
# => [7, -1]
--- XMLRPC::Client#multicall2( *methods )
Same as ((<XMLRPC::Client#multicall>)), but returns like ((<XMLRPC::Client#call2>)) two parameters
instead of raising an (({XMLRPC::FaultException})).
--- XMLRPC::Client#proxy( prefix, *args )
Returns an object of class (({XMLRPC::Client::Proxy})), initialized with
((|prefix|)) and ((|args|)). A proxy object returned by this method behaves
like ((<XMLRPC::Client#call>)), i.e. a call on that object will raise a
(({XMLRPC::FaultException})) when a fault-structure is returned by that call.
--- XMLRPC::Client#proxy2( prefix, *args )
Almost the same like ((<XMLRPC::Client#proxy>)) only that a call on the returned
(({XMLRPC::Client::Proxy})) object behaves like ((<XMLRPC::Client#call2>)), i.e.
a call on that object will return two parameters.
--- XMLRPC::Client#call_async(...)
--- XMLRPC::Client#call2_async(...)
--- XMLRPC::Client#multicall_async(...)
--- XMLRPC::Client#multicall2_async(...)
--- XMLRPC::Client#proxy_async(...)
--- XMLRPC::Client#proxy2_async(...)
In contrast to corresponding methods without "_async", these can be
called concurrently and use for each request a new connection, where the
non-asynchronous counterparts use connection-alive (one connection for all requests)
if possible.
Note, that you have to use Threads to call these methods concurrently.
The following example calls two methods concurrently:
Thread.new {
p client.call_async("michael.add", 4, 5)
}
Thread.new {
p client.call_async("michael.div", 7, 9)
}
--- XMLRPC::Client#timeout
--- XMLRPC::Client#user
--- XMLRPC::Client#password
Return the corresponding attributes.
--- XMLRPC::Client#timeout= (new_timeout)
--- XMLRPC::Client#user= (new_user)
--- XMLRPC::Client#password= (new_password)
Set the corresponding attributes.
--- XMLRPC::Client#set_writer( writer )
Sets the XML writer to use for generating XML output.
Should be an instance of a class from module (({XMLRPC::XMLWriter})).
If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used.
--- XMLRPC::Client#set_parser( parser )
Sets the XML parser to use for parsing XML documents.
Should be an instance of a class from module (({XMLRPC::XMLParser})).
If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used.
--- XMLRPC::Client#cookie
--- XMLRPC::Client#cookie= (cookieString)
Get and set the HTTP Cookie header.
--- XMLRPC::Client#http_header_extra= (additionalHeaders)
Set extra HTTP headers that are included in the request.
--- XMLRPC::Client#http_header_extra
Access the via ((<XMLRPC::Client#http_header_extra=>)) assigned header.
--- XMLRPC::Client#http_last_response
Returns the (({Net::HTTPResponse})) object of the last RPC.
= XMLRPC::Client::Proxy
== Synopsis
require "xmlrpc/client"
server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
michael = server.proxy("michael")
michael2 = server.proxy("michael", 4)
# both calls should return the same value '9'.
p michael.add(4,5)
p michael2.add(5)
== Description
Class (({XMLRPC::Client::Proxy})) makes XML-RPC calls look nicer!
You can call any method onto objects of that class - the object handles
(({method_missing})) and will forward the method call to a XML-RPC server.
Don't use this class directly, but use instead method ((<XMLRPC::Client#proxy>)) or
((<XMLRPC::Client#proxy2>)).
== Class Methods
--- XMLRPC::Client::Proxy.new( server, prefix, args=[], meth=:call, delim="." )
Creates an object which provides (({method_missing})).
((|server|)) must be of type (({XMLRPC::Client})), which is the XML-RPC server to be used
for a XML-RPC call. ((|prefix|)) and ((|delim|)) will be prepended to the methodname
called onto this object.
Parameter ((|meth|)) is the method (call, call2, call_async, call2_async) to use for
a RPC.
((|args|)) are arguments which are automatically given
to every XML-RPC call before the arguments provides through (({method_missing})).
== Instance Methods
Every method call is forwarded to the XML-RPC server defined in ((<new|XMLRPC::Client::Proxy#new>)).
Note: Inherited methods from class (({Object})) cannot be used as XML-RPC names, because they get around
(({method_missing})).
= History
$Id$
=end
# xmlrpc/client.rb
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
#
# Released under the same term of license as Ruby.
#
# History
# $Id$
#
require "xmlrpc/parser"
require "xmlrpc/create"
require "xmlrpc/config"
......
require "net/http"
require "uri"
module XMLRPC
module XMLRPC # :nodoc:
# Provides remote procedure calls to a XML-RPC server.
#
# After setting the connection-parameters with XMLRPC::Client.new which
# creates a new XMLRPC::Client instance, you can execute a remote procedure
# by sending the XMLRPC::Client#call or XMLRPC::Client#call2
# message to this new instance.
#
# The given parameters indicate which method to call on the remote-side and
# of course the parameters for the remote procedure.
#
# require "xmlrpc/client"
#
# server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
# begin
# param = server.call("michael.add", 4, 5)
# puts "4 + 5 = #{param}"
# rescue XMLRPC::FaultException => e
# puts "Error:"
# puts e.faultCode
# puts e.faultString
# end
#
# or
#
# require "xmlrpc/client"
#
# server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80)
# ok, param = server.call2("michael.add", 4, 5)
# if ok then
# puts "4 + 5 = #{param}"
# else
# puts "Error:"
# puts param.faultCode
# puts param.faultString
# end
class Client
USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})"
......
include ParserWriterChooseMixin
include ParseContentType
# Constructors -------------------------------------------------------------------
# Creates an object which represents the remote XML-RPC server on the
# given +host+. If the server is CGI-based, +path+ is the
# path to the CGI-script, which will be called, otherwise (in the
# case of a standalone server) +path+ should be <tt>"/RPC2"</tt>.
# +port+ is the port on which the XML-RPC server listens.
#
# If +proxy_host+ is given, then a proxy server listening at
# +proxy_host+ is used. +proxy_port+ is the port of the
# proxy server.
#
# Default values for +host+, +path+ and +port+ are 'localhost', '/RPC2' and
# '80' respectively using SSL '443'.
#
# If +user+ and +password+ are given, each time a request is sent,
# an Authorization header is sent. Currently only Basic Authentication is
# implemented, no Digest.
#
# If +use_ssl+ is set to +true+, communication over SSL is enabled.
#
# Note, that you need the SSL package from RAA installed.
#
# Parameter +timeout+ is the time to wait for a XML-RPC response, defaults to 30.
def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil,
user=nil, password=nil, use_ssl=nil, timeout=nil)
......
@create = nil
end
class << self
def new2(uri, proxy=nil, timeout=nil)
begin
url = URI(uri)
rescue URI::InvalidURIError => e
raise ArgumentError, e.message, e.backtrace
end
unless URI::HTTP === url
raise ArgumentError, "Wrong protocol specified. Only http or https allowed!"
end
proto = url.scheme
user = url.user
passwd = url.password
host = url.host
port = url.port
path = url.path.empty? ? nil : url.request_uri
proxy_host, proxy_port = (proxy || "").split(":")
proxy_port = proxy_port.to_i if proxy_port
# Creates an object which represents the remote XML-RPC server at the
# given +uri+. The URI should have a host, port, path, user and password.
# Example: https://user:password@host:port/path
#
# Raises an ArgumentError if the +uri+ is invalid,
# or if the protocol isn't http or https.
#
# If a +proxy+ is given it should be in the form of "host:port".
#
# The optional +timeout+ defaults to 30 seconds.
def new2(uri, proxy=nil, timeout=nil)
begin
url = URI(uri)
rescue URI::InvalidURIError => e
raise ArgumentError, e.message, e.backtrace
end
self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout)
end
unless URI::HTTP === url
raise ArgumentError, "Wrong protocol specified. Only http or https allowed!"
end
alias new_from_uri new2
proto = url.scheme
user = url.user
passwd = url.password
host = url.host
port = url.port
path = url.path.empty? ? nil : url.request_uri
def new3(hash={})
proxy_host, proxy_port = (proxy || "").split(":")
proxy_port = proxy_port.to_i if proxy_port
# convert all keys into lowercase strings
h = {}
hash.each { |k,v| h[k.to_s.downcase] = v }
self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout)
end
self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'],
h['use_ssl'], h['timeout'])
end
alias new_from_uri new2
# Receives a Hash and calls XMLRPC::Client.new
# with the corresponding values.
#
# The +hash+ parameter has following case-insensitive keys:
# * host
# * path
# * port
# * proxy_host
# * proxy_port
# * user
# * password
# * use_ssl
# * timeout
def new3(hash={})
# convert all keys into lowercase strings
h = {}
hash.each { |k,v| h[k.to_s.downcase] = v }
self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'],
h['use_ssl'], h['timeout'])
end
alias new_from_hash new3
alias new_from_hash new3
end
# Attribute Accessors -------------------------------------------------------------------
# add additional HTTP headers to the request
# Add additional HTTP headers to the request
attr_accessor :http_header_extra
# makes last HTTP response accessible
# Returns the Net::HTTPResponse object of the last RPC.
attr_reader :http_last_response
# Cookie support
# Get and set the HTTP Cookie header.
attr_accessor :cookie
# Return the corresponding attributes.
attr_reader :timeout, :user, :password
# Sets the Net::HTTP#read_timeout and Net::HTTP#open_timeout to
# +new_timeout+
def timeout=(new_timeout)
@timeout = new_timeout
@http.read_timeout = @timeout
@http.open_timeout = @timeout
end
# Changes the user for the Basic Authentication header to +new_user+
def user=(new_user)
@user = new_user
set_auth
end
# Changes the password for the Basic Authentication header to
# +new_password+
def password=(new_password)
@password = new_password
set_auth
end
# Call methods --------------------------------------------------------------
# Invokes the method named +method+ with the parameters given by
# +args+ on the XML-RPC server.
#
# The +method+ parameter is converted into a String and should
# be a valid XML-RPC method-name.
#
# Each parameter of +args+ must be of one of the following types,
# where Hash, Struct and Array can contain any of these listed _types_:
#
# * Fixnum, Bignum
# * TrueClass, FalseClass, +true+, +false+
# * String, Symbol
# * Float
# * Hash, Struct
# * Array
# * Date, Time, XMLRPC::DateTime
# * XMLRPC::Base64
# * A Ruby object which class includes XMLRPC::Marshallable
# (only if Config::ENABLE_MARSHALLABLE is +true+).
# That object is converted into a hash, with one additional key/value
# pair <code>___class___</code> which contains the class name
# for restoring that object later.
#
# The method returns the return-value from the Remote Procedure Call.
#
# The type of the return-value is one of the types shown above.
#
# A Bignum is only allowed when it fits in 32-bit. A XML-RPC
# +dateTime.iso8601+ type is always returned as a XMLRPC::DateTime object.
# Struct is never returned, only a Hash, the same for a Symbol, where as a
# String is always returned. XMLRPC::Base64 is returned as a String from
# xmlrpc4r version 1.6.1 on.
#
# If the remote procedure returned a fault-structure, then a
# XMLRPC::FaultException exception is raised, which has two accessor-methods
# +faultCode+ an Integer, and +faultString+ a String.
def call(method, *args)
ok, param = call2(method, *args)
if ok
......
end
end
# The difference between this method and XMLRPC::Client#call is, that
# this method will <b>NOT</b> raise a XMLRPC::FaultException exception.
#
# The method returns an array of two values. The first value indicates if
# the second value is +true+ or an XMLRPC::FaultException.
#
# Both are explained in XMLRPC::Client#call.
#
# Simple to remember: The "2" in "call2" denotes the number of values it returns.
def call2(method, *args)
request = create().methodCall(method, *args)
data = do_rpc(request, false)
parser().parseMethodResponse(data)
end
# Similar to XMLRPC::Client#call, however can be called concurrently and
# use a new connection for each request. In contrast to the corresponding
# method without the +_async+ suffix, which use connect-alive (one
# connection for all requests).
#
# Note, that you have to use Thread to call these methods concurrently.
# The following example calls two methods concurrently:
#
# Thread.new {
# p client.call_async("michael.add", 4, 5)
# }
#
# Thread.new {
# p client.call_async("michael.div", 7, 9)
# }
#
def call_async(method, *args)
ok, param = call2_async(method, *args)
if ok
......
end
end
# Same as XMLRPC::Client#call2, but can be called concurrently.
#
# See also XMLRPC::Client#call_async
def call2_async(method, *args)
request = create().methodCall(method, *args)
data = do_rpc(request, true)
parser().parseMethodResponse(data)
end
# Multicall methods --------------------------------------------------------------
# You can use this method to execute several methods on a XMLRPC server
# which support the multi-call extension.
#
# s.multicall(
# ['michael.add', 3, 4],
# ['michael.sub', 4, 5]
# )
# # => [7, -1]
def multicall(*methods)
ok, params = multicall2(*methods)
if ok
......
end
end
# Same as XMLRPC::Client#multicall, but returns two parameters instead of
# raising an XMLRPC::FaultException.
#
# See XMLRPC::Client#call2
def multicall2(*methods)
gen_multicall(methods, false)
end
# Similar to XMLRPC::Client#multicall, however can be called concurrently and
# use a new connection for each request. In contrast to the corresponding
# method without the +_async+ suffix, which use connect-alive (one
# connection for all requests).
#
# Note, that you have to use Thread to call these methods concurrently.
# The following example calls two methods concurrently:
#
# Thread.new {
# p client.multicall_async("michael.add", 4, 5)
# }
#
# Thread.new {
# p client.multicall_async("michael.div", 7, 9)
# }
#
def multicall_async(*methods)
ok, params = multicall2_async(*methods)
if ok
......
end
end
# Same as XMLRPC::Client#multicall2, but can be called concurrently.
#
# See also XMLRPC::Client#multicall_async
def multicall2_async(*methods)
gen_multicall(methods, true)
end
# Proxy generating methods ------------------------------------------
# Returns an object of class XMLRPC::Client::Proxy, initialized with
# +prefix+ and +args+.
#
# A proxy object returned by this method behaves like XMLRPC::Client#call,
# i.e. a call on that object will raise a XMLRPC::FaultException when a
# fault-structure is returned by that call.
def proxy(prefix=nil, *args)
Proxy.new(self, prefix, args, :call)
end
# Almost the same like XMLRPC::Client#proxy only that a call on the returned
# XMLRPC::Client::Proxy object will return two parameters.
#
# See XMLRPC::Client#call2
def proxy2(prefix=nil, *args)
Proxy.new(self, prefix, args, :call2)
end
# Similar to XMLRPC::Client#proxy, however can be called concurrently and
# use a new connection for each request. In contrast to the corresponding
# method without the +_async+ suffix, which use connect-alive (one
# connection for all requests).
#
# Note, that you have to use Thread to call these methods concurrently.
# The following example calls two methods concurrently:
#
# Thread.new {
# p client.proxy_async("michael.add", 4, 5)
# }
#
# Thread.new {
# p client.proxy_async("michael.div", 7, 9)
# }
#
def proxy_async(prefix=nil, *args)
... This diff was truncated because it exceeds the maximum size that can be displayed.
(1-1/2)