diff options
Diffstat (limited to 'lib/xmlrpc/server.rb')
-rw-r--r-- | lib/xmlrpc/server.rb | 557 |
1 files changed, 243 insertions, 314 deletions
diff --git a/lib/xmlrpc/server.rb b/lib/xmlrpc/server.rb index b7215385ad..839353da1a 100644 --- a/lib/xmlrpc/server.rb +++ b/lib/xmlrpc/server.rb @@ -1,146 +1,7 @@ -=begin -= xmlrpc/server.rb -Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de) - -Released under the same term of license as Ruby. - -= Classes -* ((<XMLRPC::BasicServer>)) -* ((<XMLRPC::CGIServer>)) -* ((<XMLRPC::ModRubyServer>)) -* ((<XMLRPC::Server>)) -* ((<XMLRPC::WEBrickServlet>)) - -= XMLRPC::BasicServer -== Description -Is the base class for all XML-RPC server-types (CGI, standalone). -You can add handler and set a default handler. -Do not use this server, as this is/should be an abstract class. - -=== How the method to call is found -The arity (number of accepted arguments) of a handler (method or (({Proc})) object) is -compared to the given arguments submitted by the client for a RPC ((-Remote Procedure Call-)). -A handler is only called if it accepts the number of arguments, otherwise the search -for another handler will go on. When at the end no handler was found, -the ((<default_handler|XMLRPC::BasicServer#set_default_handler>)) will be called. -With this technique it is possible to do overloading by number of parameters, but -only for (({Proc})) handler, because you cannot define two methods of the same name in -the same class. - - -== Class Methods ---- XMLRPC::BasicServer.new( class_delim="." ) - Creates a new (({XMLRPC::BasicServer})) instance, which should not be - done, because (({XMLRPC::BasicServer})) is an abstract class. This - method should be called from a subclass indirectly by a (({super})) call - in the method (({initialize})). The paramter ((|class_delim|)) is used - in ((<add_handler|XMLRPC::BasicServer#add_handler>)) when an object is - added as handler, to delimit the object-prefix and the method-name. - -== Instance Methods ---- XMLRPC::BasicServer#add_handler( name, signature=nil, help=nil ) { aBlock } - Adds ((|aBlock|)) to the list of handlers, with ((|name|)) as the name of the method. - Parameters ((|signature|)) and ((|help|)) are used by the Introspection method if specified, - where ((|signature|)) is either an Array containing strings each representing a type of it's - signature (the first is the return value) or an Array of Arrays if the method has multiple - signatures. Value type-names are "int, boolean, double, string, dateTime.iso8601, base64, array, struct". - - Parameter ((|help|)) is a String with informations about how to call this method etc. - - A handler method or code-block can return the types listed at - ((<XMLRPC::Client#call|URL:client.html#index:0>)). - When a method fails, it can tell it the client by throwing an - (({XMLRPC::FaultException})) like in this example: - s.add_handler("michael.div") do |a,b| - if b == 0 - raise XMLRPC::FaultException.new(1, "division by zero") - else - a / b - end - end - The client gets in the case of (({b==0})) an object back of type - (({XMLRPC::FaultException})) that has a ((|faultCode|)) and ((|faultString|)) - field. - ---- XMLRPC::BasicServer#add_handler( prefix, obj ) - This is the second form of ((<add_handler|XMLRPC::BasicServer#add_handler>)). - To add an object write: - server.add_handler("michael", MyHandlerClass.new) - All public methods of (({MyHandlerClass})) are accessible to - the XML-RPC clients by (('michael."name of method"')). This is - where the ((|class_delim|)) in ((<new|XMLRPC::BasicServer.new>)) - has it's role, a XML-RPC method-name is defined by - ((|prefix|)) + ((|class_delim|)) + (('"name of method"')). - ---- XMLRPC::BasicServer#add_handler( interface, obj ) - This is the third form of ((<add_handler|XMLRPC::BasicServer#add_handler>)). - - Use (({XMLRPC::interface})) to generate an ServiceInterface object, which - represents an interface (with signature and help text) for a handler class. - - Parameter ((|interface|)) must be of type (({XMLRPC::ServiceInterface})). - Adds all methods of ((|obj|)) which are defined in ((|interface|)) to the - server. - - This is the recommended way of adding services to a server! - - ---- XMLRPC::BasicServer#get_default_handler - Returns the default-handler, which is called when no handler for - a method-name is found. - It is a (({Proc})) object or (({nil})). - ---- XMLRPC::BasicServer#set_default_handler ( &handler ) - Sets ((|handler|)) as the default-handler, which is called when - no handler for a method-name is found. ((|handler|)) is a code-block. - The default-handler is called with the (XML-RPC) method-name as first argument, and - the other arguments are the parameters given by the client-call. - - If no block is specified the default of (({XMLRPC::BasicServer})) is used, which raises a - XMLRPC::FaultException saying "method missing". - - ---- XMLRPC::BasicServer#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::BasicServer#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::BasicServer#add_introspection - Adds the introspection handlers "system.listMethods", "system.methodSignature" and "system.methodHelp", - where only the first one works. - ---- XMLRPC::BasicServer#add_multicall - Adds the multi-call handler "system.multicall". - ---- XMLRPC::BasicServer#get_service_hook - Returns the service-hook, which is called on each service request (RPC) unless it's (({nil})). - ---- XMLRPC::BasicServer#set_service_hook ( &handler ) - A service-hook is called for each service request (RPC). - You can use a service-hook for example to wrap existing methods and catch exceptions of them or - convert values to values recognized by XMLRPC. You can disable it by passing (({nil})) as parameter - ((|handler|)) . - - The service-hook is called with a (({Proc})) object and with the parameters for this (({Proc})). - An example: - - server.set_service_hook {|obj, *args| - begin - ret = obj.call(*args) # call the original service-method - # could convert the return value - rescue - # rescue exceptions - end - } - -=end - - +# xmlrpc/server.rb +# Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de) +# +# Released under the same term of license as Ruby. require "xmlrpc/parser" require "xmlrpc/create" @@ -149,9 +10,26 @@ require "xmlrpc/utils" # ParserWriterChooseMixin -module XMLRPC +module XMLRPC # :nodoc: +# This is the base class for all XML-RPC server-types (CGI, standalone). +# You can add handler and set a default handler. +# Do not use this server, as this is/should be an abstract class. +# +# === How the method to call is found +# The arity (number of accepted arguments) of a handler (method or Proc +# object) is compared to the given arguments submitted by the client for a +# RPC, or Remote Procedure Call. +# +# A handler is only called if it accepts the number of arguments, otherwise +# the search for another handler will go on. When at the end no handler was +# found, the default_handler, XMLRPC::BasicServer#set_default_handler will be +# called. +# +# With this technique it is possible to do overloading by number of parameters, but +# only for Proc handler, because you cannot define two methods of the same name in +# the same class. class BasicServer include ParserWriterChooseMixin @@ -167,6 +45,14 @@ class BasicServer ERR_MC_EXPECTED_STRUCT = 8 + # Creates a new XMLRPC::BasicServer instance, which should not be + # done, because XMLRPC::BasicServer is an abstract class. This + # method should be called from a subclass indirectly by a +super+ call + # in the initialize method. + # + # The paramter +class_delim+ is used by add_handler, see + # XMLRPC::BasicServer#add_handler, when an object is added as a handler, to + # delimit the object-prefix and the method-name. def initialize(class_delim=".") @handler = [] @default_handler = nil @@ -180,6 +66,52 @@ class BasicServer add_introspection if Config::ENABLE_INTROSPECTION end + # Adds +aBlock+ to the list of handlers, with +name+ as the name of + # the method. + # + # Parameters +signature+ and +help+ are used by the Introspection method if + # specified, where +signature+ is either an Array containing strings each + # representing a type of it's signature (the first is the return value) or + # an Array of Arrays if the method has multiple signatures. + # + # Value type-names are "int, boolean, double, string, dateTime.iso8601, + # base64, array, struct". + # + # Parameter +help+ is a String with information about how to call this method etc. + # + # When a method fails, it can tell the client by throwing an + # XMLRPC::FaultException like in this example: + # + # s.add_handler("michael.div") do |a,b| + # if b == 0 + # raise XMLRPC::FaultException.new(1, "division by zero") + # else + # a / b + # end + # end + # + # In the case of <code>b==0</code> the client gets an object back of type + # XMLRPC::FaultException that has a +faultCode+ and +faultString+ field. + # + # This is the second form of ((<add_handler|XMLRPC::BasicServer#add_handler>)). + # To add an object write: + # + # server.add_handler("michael", MyHandlerClass.new) + # + # All public methods of MyHandlerClass are accessible to + # the XML-RPC clients by <code>michael."name of method"</code>. This is + # where the +class_delim+ in XMLRPC::BasicServer.new plays it's role, a + # XML-RPC method-name is defined by +prefix+ + +class_delim+ + <code>"name + # of method"</code>. + # + # The third form of +add_handler is to use XMLRPC::Service::Interface to + # generate an object, which represents an interface (with signature and + # help text) for a handler class. + # + # The +interface+ parameter must be an instance of XMLRPC::Service::Interface. + # Adds all methods of +obj+ which are defined in the +interface+ to the server. + # + # This is the recommended way of adding services to a server! def add_handler(prefix, obj_or_signature=nil, help=nil, &block) if block_given? # proc-handler @@ -200,24 +132,62 @@ class BasicServer self end + # Returns the service-hook, which is called on each service request (RPC) + # unless it's +nil+. def get_service_hook @service_hook end + # A service-hook is called for each service request (RPC). + # + # You can use a service-hook for example to wrap existing methods and catch + # exceptions of them or convert values to values recognized by XMLRPC. + # + # You can disable it by passing +nil+ as the +handler+ parameter. + # + # The service-hook is called with a Proc object along with any parameters. + # + # An example: + # + # server.set_service_hook {|obj, *args| + # begin + # ret = obj.call(*args) # call the original service-method + # # could convert the return value + # rescue + # # rescue exceptions + # end + # } + # def set_service_hook(&handler) @service_hook = handler self end + # Returns the default-handler, which is called when no handler for + # a method-name is found. + # + # It is either a Proc object or +nil+. def get_default_handler @default_handler end - def set_default_handler (&handler) + # Sets +handler+ as the default-handler, which is called when + # no handler for a method-name is found. + # + # +handler+ is a code-block. + # + # The default-handler is called with the (XML-RPC) method-name as first + # argument, and the other arguments are the parameters given by the + # client-call. + # + # If no block is specified the default of XMLRPC::BasicServer is + # used, which raises a XMLRPC::FaultException saying "method missing". + def set_default_handler(&handler) @default_handler = handler self end + # Adds the multi-call handler <code>"system.multicall"</code>. def add_multicall add_handler("system.multicall", %w(array array), "Multicall Extension") do |arrStructs| unless arrStructs.is_a? Array @@ -260,6 +230,9 @@ class BasicServer self end + # Adds the introspection handlers <code>"system.listMethods"</code>, + # <code>"system.methodSignature"</code> and + # <code>"system.methodHelp"</code>, where only the first one works. def add_introspection add_handler("system.listMethods",%w(array), "List methods available on this XML-RPC server") do methods = [] @@ -312,15 +285,12 @@ class BasicServer handle(method, *params) end - private # -------------------------------------------------------------- + private def multicall_fault(nr, str) {"faultCode" => nr, "faultString" => str} end - # - # method dispatch - # def dispatch(methodname, *args) for name, obj in @handler if obj.kind_of? Proc @@ -348,9 +318,7 @@ class BasicServer end - # - # returns true, if the arity of "obj" matches - # + # Returns +true+, if the arity of +obj+ matches +n_args+ def check_arity(obj, n_args) ary = obj.arity @@ -373,9 +341,6 @@ class BasicServer end end - # - # - # def handle(methodname, *args) create().methodResponse(*call_method(methodname, *args)) end @@ -384,57 +349,44 @@ class BasicServer end -=begin -= XMLRPC::CGIServer -== Synopsis - require "xmlrpc/server" - - s = XMLRPC::CGIServer.new - - s.add_handler("michael.add") do |a,b| - a + b - end - - s.add_handler("michael.div") do |a,b| - if b == 0 - raise XMLRPC::FaultException.new(1, "division by zero") - else - a / b - end - end - - s.set_default_handler do |name, *args| - raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + - " or wrong number of parameters!") - end - - s.serve - -== Description -Implements a CGI-based XML-RPC server. - -== Superclass -((<XMLRPC::BasicServer>)) - -== Class Methods ---- XMLRPC::CGIServer.new( *a ) - Creates a new (({XMLRPC::CGIServer})) instance. All parameters given - are by-passed to ((<XMLRPC::BasicServer.new>)). You can only create - ((*one*)) (({XMLRPC::CGIServer})) instance, because more than one makes - no sense. - -== Instance Methods ---- XMLRPC::CGIServer#serve - Call this after you have added all you handlers to the server. - This method processes a XML-RPC methodCall and sends the answer - back to the client. - Make sure that you don't write to standard-output in a handler, or in - any other part of your program, this would case a CGI-based server to fail! -=end - +# Implements a CGI-based XML-RPC server. +# +# require "xmlrpc/server" +# +# s = XMLRPC::CGIServer.new +# +# s.add_handler("michael.add") do |a,b| +# a + b +# end +# +# s.add_handler("michael.div") do |a,b| +# if b == 0 +# raise XMLRPC::FaultException.new(1, "division by zero") +# else +# a / b +# end +# end +# +# s.set_default_handler do |name, *args| +# raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + +# " or wrong number of parameters!") +# end +# +# s.serve +# +# +# <b>Note:</b> Make sure that you don't write to standard-output in a +# handler, or in any other part of your program, this would cause a CGI-based +# server to fail! class CGIServer < BasicServer @@obj = nil + # Creates a new XMLRPC::CGIServer instance. + # + # All parameters given are by-passed to XMLRPC::BasicServer.new. + # + # You can only create <b>one</b> XMLRPC::CGIServer instance, because more + # than one makes no sense. def CGIServer.new(*a) @@obj = super(*a) if @@obj.nil? @@obj @@ -444,6 +396,10 @@ class CGIServer < BasicServer super(*a) end + # Call this after you have added all you handlers to the server. + # + # This method processes a XML-RPC method call and sends the answer + # back to the client. def serve catch(:exit_serve) { length = ENV['CONTENT_LENGTH'].to_i @@ -498,24 +454,24 @@ class CGIServer < BasicServer end -=begin -= XMLRPC::ModRubyServer -== Description -Implements a XML-RPC server, which works with Apache mod_ruby. - -Use it in the same way as CGIServer! - -== Superclass -((<XMLRPC::BasicServer>)) -=end +# Implements a XML-RPC server, which works with Apache mod_ruby. +# +# Use it in the same way as XMLRPC::CGIServer! class ModRubyServer < BasicServer + # Creates a new XMLRPC::ModRubyServer instance. + # + # All parameters given are by-passed to XMLRPC::BasicServer.new. def initialize(*a) @ap = Apache::request super(*a) end + # Call this after you have added all you handlers to the server. + # + # This method processes a XML-RPC method call and sends the answer + # back to the client. def serve catch(:exit_serve) { header = {} @@ -574,63 +530,47 @@ class ModRubyServer < BasicServer end -=begin -= XMLRPC::Server -== Synopsis - require "xmlrpc/server" - - s = XMLRPC::Server.new(8080) - - s.add_handler("michael.add") do |a,b| - a + b - end - - s.add_handler("michael.div") do |a,b| - if b == 0 - raise XMLRPC::FaultException.new(1, "division by zero") - else - a / b - end - end - - s.set_default_handler do |name, *args| - raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + - " or wrong number of parameters!") - end - - s.serve - -== Description -Implements a standalone XML-RPC server. The method (({serve}))) is left if a SIGHUP is sent to the -program. - -== Superclass -((<XMLRPC::WEBrickServlet>)) - -== Class Methods ---- XMLRPC::Server.new( port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a ) - Creates a new (({XMLRPC::Server})) instance, which is a XML-RPC server listening on - port ((|port|)) and accepts requests for the host ((|host|)), which is by default only the localhost. - The server is not started, to start it you have to call ((<serve|XMLRPC::Server#serve>)). - - Parameters ((|audit|)) and ((|debug|)) are obsolete! - - All additionally given parameters in ((|*a|)) are by-passed to ((<XMLRPC::BasicServer.new>)). - -== Instance Methods ---- XMLRPC::Server#serve - Call this after you have added all you handlers to the server. - This method starts the server to listen for XML-RPC requests and answer them. - ---- XMLRPC::Server#shutdown - Stops and shuts the server down. - -=end class WEBrickServlet < BasicServer; end # forward declaration +# Implements a standalone XML-RPC server. The method XMLRPC::Server#serve is +# left if a SIGHUP is sent to the program. +# +# require "xmlrpc/server" +# +# s = XMLRPC::Server.new(8080) +# +# s.add_handler("michael.add") do |a,b| +# a + b +# end +# +# s.add_handler("michael.div") do |a,b| +# if b == 0 +# raise XMLRPC::FaultException.new(1, "division by zero") +# else +# a / b +# end +# end +# +# s.set_default_handler do |name, *args| +# raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + +# " or wrong number of parameters!") +# end +# +# s.serve class Server < WEBrickServlet + # Creates a new XMLRPC::Server instance, which is a XML-RPC server + # listening on the given +port+ and accepts requests for the given +host+, + # which is +localhost+ by default. + # + # The server is not started, to start it you have to call + # XMLRPC::Server#serve. + # + # The optional +audit+ and +debug+ parameters are obsolete! + # + # All additionally provided parameters in <code>*a</code> are by-passed to + # XMLRPC::BasicServer.new. def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a) super(*a) require 'webrick' @@ -639,6 +579,8 @@ class Server < WEBrickServlet @server.mount("/", self) end + # Call this after you have added all you handlers to the server. + # This method starts the server to listen for XML-RPC requests and answer them. def serve signals = %w[INT TERM HUP] & Signal.list.keys signals.each { |signal| trap(signal) { @server.shutdown } } @@ -646,60 +588,42 @@ class Server < WEBrickServlet @server.start end + # Stops and shuts the server down. def shutdown @server.shutdown end end -=begin -= XMLRPC::WEBrickServlet -== Synopsis - - require "webrick" - require "xmlrpc/server" - - s = XMLRPC::WEBrickServlet.new - s.add_handler("michael.add") do |a,b| - a + b - end - - s.add_handler("michael.div") do |a,b| - if b == 0 - raise XMLRPC::FaultException.new(1, "division by zero") - else - a / b - end - end - - s.set_default_handler do |name, *args| - raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + - " or wrong number of parameters!") - end - - httpserver = WEBrick::HTTPServer.new(:Port => 8080) - httpserver.mount("/RPC2", s) - trap("HUP") { httpserver.shutdown } # use 1 instead of "HUP" on Windows - httpserver.start - -== Instance Methods - ---- XMLRPC::WEBrickServlet#set_valid_ip( *ip_addr ) - Specifies the valid IP addresses that are allowed to connect to the server. - Each IP is either a (({String})) or a (({Regexp})). - ---- XMLRPC::WEBrickServlet#get_valid_ip - Return the via method ((<set_valid_ip|XMLRPC::Server#set_valid_ip>)) specified - valid IP addresses. - -== Description -Implements a servlet for use with WEBrick, a pure Ruby (HTTP-) server framework. - -== Superclass -((<XMLRPC::BasicServer>)) - -=end +# Implements a servlet for use with WEBrick, a pure Ruby (HTTP) server +# framework. +# +# require "webrick" +# require "xmlrpc/server" +# +# s = XMLRPC::WEBrickServlet.new +# s.add_handler("michael.add") do |a,b| +# a + b +# end +# +# s.add_handler("michael.div") do |a,b| +# if b == 0 +# raise XMLRPC::FaultException.new(1, "division by zero") +# else +# a / b +# end +# end +# +# s.set_default_handler do |name, *args| +# raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + +# " or wrong number of parameters!") +# end +# +# httpserver = WEBrick::HTTPServer.new(:Port => 8080) +# httpserver.mount("/RPC2", s) +# trap("HUP") { httpserver.shutdown } # use 1 instead of "HUP" on Windows +# httpserver.start class WEBrickServlet < BasicServer def initialize(*a) super @@ -707,8 +631,7 @@ class WEBrickServlet < BasicServer @valid_ip = nil end - # deprecated from WEBrick/1.2.2. - # but does not break anything. + # Deprecated from WEBrick/1.2.2, but does not break anything. def require_path_info? false end @@ -718,6 +641,9 @@ class WEBrickServlet < BasicServer self end + # Specifies the valid IP addresses that are allowed to connect to the server. + # + # Each IP is either a String or a Regexp. def set_valid_ip(*ip_addr) if ip_addr.size == 1 and ip_addr[0].nil? @valid_ip = nil @@ -726,6 +652,9 @@ class WEBrickServlet < BasicServer end end + # Return the valid IP addresses that are allowed to connect to the server. + # + # See also, XMLRPC::Server#set_valid_ip def get_valid_ip @valid_ip end |