PageRenderTime 197ms CodeModel.GetById 132ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/tools/Ruby/lib/ruby/1.8/xmlrpc/server.rb

http://github.com/agross/netopenspace
Ruby | 782 lines | 351 code | 108 blank | 323 comment | 52 complexity | 5ab0fd849ba51e84a7b3b098a772d544 MD5 | raw file
  1=begin
  2= xmlrpc/server.rb
  3Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de)
  4
  5Released under the same term of license as Ruby.
  6
  7= Classes
  8* ((<XMLRPC::BasicServer>))
  9* ((<XMLRPC::CGIServer>))
 10* ((<XMLRPC::ModRubyServer>))
 11* ((<XMLRPC::Server>))
 12* ((<XMLRPC::WEBrickServlet>))
 13
 14= XMLRPC::BasicServer
 15== Description
 16Is the base class for all XML-RPC server-types (CGI, standalone).
 17You can add handler and set a default handler. 
 18Do not use this server, as this is/should be an abstract class.
 19
 20=== How the method to call is found
 21The arity (number of accepted arguments) of a handler (method or (({Proc})) object) is 
 22compared to the given arguments submitted by the client for a RPC ((-Remote Procedure Call-)). 
 23A handler is only called if it accepts the number of arguments, otherwise the search 
 24for another handler will go on. When at the end no handler was found, 
 25the ((<default_handler|XMLRPC::BasicServer#set_default_handler>)) will be called.
 26With this technique it is possible to do overloading by number of parameters, but
 27only for (({Proc})) handler, because you cannot define two methods of the same name in
 28the same class. 
 29
 30
 31== Class Methods
 32--- XMLRPC::BasicServer.new( class_delim="." )
 33    Creates a new (({XMLRPC::BasicServer})) instance, which should not be 
 34    done, because (({XMLRPC::BasicServer})) is an abstract class. This
 35    method should be called from a subclass indirectly by a (({super})) call
 36    in the method (({initialize})). The paramter ((|class_delim|)) is used
 37    in ((<add_handler|XMLRPC::BasicServer#add_handler>)) when an object is
 38    added as handler, to delimit the object-prefix and the method-name.
 39
 40== Instance Methods
 41--- XMLRPC::BasicServer#add_handler( name, signature=nil, help=nil ) { aBlock }
 42    Adds ((|aBlock|)) to the list of handlers, with ((|name|)) as the name of the method.
 43    Parameters ((|signature|)) and ((|help|)) are used by the Introspection method if specified, 
 44    where ((|signature|)) is either an Array containing strings each representing a type of it's 
 45    signature (the first is the return value) or an Array of Arrays if the method has multiple 
 46    signatures. Value type-names are "int, boolean, double, string, dateTime.iso8601, base64, array, struct".
 47
 48    Parameter ((|help|)) is a String with informations about how to call this method etc.
 49
 50    A handler method or code-block can return the types listed at
 51    ((<XMLRPC::Client#call|URL:client.html#index:0>)). 
 52    When a method fails, it can tell it the client by throwing an 
 53    (({XMLRPC::FaultException})) like in this example:
 54        s.add_handler("michael.div") do |a,b|
 55          if b == 0
 56            raise XMLRPC::FaultException.new(1, "division by zero")
 57          else
 58            a / b 
 59          end
 60        end 
 61    The client gets in the case of (({b==0})) an object back of type
 62    (({XMLRPC::FaultException})) that has a ((|faultCode|)) and ((|faultString|))
 63    field.
 64
 65--- XMLRPC::BasicServer#add_handler( prefix, obj )
 66    This is the second form of ((<add_handler|XMLRPC::BasicServer#add_handler>)).
 67    To add an object write:
 68        server.add_handler("michael", MyHandlerClass.new)
 69    All public methods of (({MyHandlerClass})) are accessible to
 70    the XML-RPC clients by (('michael."name of method"')). This is 
 71    where the ((|class_delim|)) in ((<new|XMLRPC::BasicServer.new>)) 
 72    has it's role, a XML-RPC method-name is defined by 
 73    ((|prefix|)) + ((|class_delim|)) + (('"name of method"')). 
 74
 75--- XMLRPC::BasicServer#add_handler( interface, obj )
 76    This is the third form of ((<add_handler|XMLRPC::BasicServer#add_handler>)).
 77
 78    Use (({XMLRPC::interface})) to generate an ServiceInterface object, which
 79    represents an interface (with signature and help text) for a handler class.
 80
 81    Parameter ((|interface|)) must be of type (({XMLRPC::ServiceInterface})).
 82    Adds all methods of ((|obj|)) which are defined in ((|interface|)) to the
 83    server.
 84
 85    This is the recommended way of adding services to a server!
 86
 87
 88--- XMLRPC::BasicServer#get_default_handler
 89    Returns the default-handler, which is called when no handler for
 90    a method-name is found.
 91    It is a (({Proc})) object or (({nil})).
 92
 93--- XMLRPC::BasicServer#set_default_handler ( &handler )
 94    Sets ((|handler|)) as the default-handler, which is called when 
 95    no handler for a method-name is found. ((|handler|)) is a code-block.
 96    The default-handler is called with the (XML-RPC) method-name as first argument, and
 97    the other arguments are the parameters given by the client-call.
 98  
 99    If no block is specified the default of (({XMLRPC::BasicServer})) is used, which raises a
100    XMLRPC::FaultException saying "method missing".
101
102
103--- XMLRPC::BasicServer#set_writer( writer )
104    Sets the XML writer to use for generating XML output.
105    Should be an instance of a class from module (({XMLRPC::XMLWriter})).
106    If this method is not called, then (({XMLRPC::Config::DEFAULT_WRITER})) is used. 
107
108--- XMLRPC::BasicServer#set_parser( parser )
109    Sets the XML parser to use for parsing XML documents.
110    Should be an instance of a class from module (({XMLRPC::XMLParser})).
111    If this method is not called, then (({XMLRPC::Config::DEFAULT_PARSER})) is used.
112
113--- XMLRPC::BasicServer#add_introspection
114    Adds the introspection handlers "system.listMethods", "system.methodSignature" and "system.methodHelp", 
115    where only the first one works.
116
117--- XMLRPC::BasicServer#add_multicall
118    Adds the multi-call handler "system.multicall".
119
120--- XMLRPC::BasicServer#get_service_hook
121    Returns the service-hook, which is called on each service request (RPC) unless it's (({nil})).
122
123--- XMLRPC::BasicServer#set_service_hook ( &handler )
124    A service-hook is called for each service request (RPC).
125    You can use a service-hook for example to wrap existing methods and catch exceptions of them or
126    convert values to values recognized by XMLRPC. You can disable it by passing (({nil})) as parameter  
127    ((|handler|)) .
128
129    The service-hook is called with a (({Proc})) object and with the parameters for this (({Proc})).
130    An example:
131
132       server.set_service_hook {|obj, *args|
133         begin
134           ret = obj.call(*args)  # call the original service-method
135           # could convert the return value 
136         resuce
137           # rescue exceptions
138         end
139       }
140
141=end
142
143
144
145require "xmlrpc/parser"
146require "xmlrpc/create"
147require "xmlrpc/config"
148require "xmlrpc/utils"         # ParserWriterChooseMixin
149
150
151
152module XMLRPC
153
154
155class BasicServer
156
157  include ParserWriterChooseMixin
158  include ParseContentType
159
160  ERR_METHOD_MISSING        = 1 
161  ERR_UNCAUGHT_EXCEPTION    = 2
162  ERR_MC_WRONG_PARAM        = 3
163  ERR_MC_MISSING_PARAMS     = 4
164  ERR_MC_MISSING_METHNAME   = 5
165  ERR_MC_RECURSIVE_CALL     = 6
166  ERR_MC_WRONG_PARAM_PARAMS = 7
167  ERR_MC_EXPECTED_STRUCT    = 8
168
169
170  def initialize(class_delim=".")
171    @handler = []
172    @default_handler = nil 
173    @service_hook = nil
174
175    @class_delim = class_delim
176    @create = nil
177    @parser = nil
178
179    add_multicall     if Config::ENABLE_MULTICALL
180    add_introspection if Config::ENABLE_INTROSPECTION
181  end
182
183  def add_handler(prefix, obj_or_signature=nil, help=nil, &block)
184    if block_given?
185      # proc-handler
186      @handler << [prefix, block, obj_or_signature, help]   
187    else
188      if prefix.kind_of? String
189        # class-handler
190        raise ArgumentError, "Expected non-nil value" if obj_or_signature.nil?
191        @handler << [prefix + @class_delim, obj_or_signature]
192      elsif prefix.kind_of? XMLRPC::Service::BasicInterface
193        # class-handler with interface
194        # add all methods
195        @handler += prefix.get_methods(obj_or_signature, @class_delim)
196      else
197        raise ArgumentError, "Wrong type for parameter 'prefix'"
198      end
199    end
200    self
201  end
202
203  def get_service_hook
204    @service_hook
205  end
206
207  def set_service_hook(&handler)
208    @service_hook = handler
209    self
210  end
211 
212  def get_default_handler
213    @default_handler
214  end
215
216  def set_default_handler (&handler)
217    @default_handler = handler
218    self
219  end  
220
221  def add_multicall
222    add_handler("system.multicall", %w(array array), "Multicall Extension") do |arrStructs|
223      unless arrStructs.is_a? Array 
224        raise XMLRPC::FaultException.new(ERR_MC_WRONG_PARAM, "system.multicall expects an array")
225      end
226
227      arrStructs.collect {|call|
228        if call.is_a? Hash
229          methodName = call["methodName"]
230          params     = call["params"]  
231
232          if params.nil?
233            multicall_fault(ERR_MC_MISSING_PARAMS, "Missing params")
234          elsif methodName.nil?
235            multicall_fault(ERR_MC_MISSING_METHNAME, "Missing methodName")
236          else
237            if methodName == "system.multicall"
238              multicall_fault(ERR_MC_RECURSIVE_CALL, "Recursive system.multicall forbidden")
239            else
240              unless params.is_a? Array
241                multicall_fault(ERR_MC_WRONG_PARAM_PARAMS, "Parameter params have to be an Array")
242              else
243                ok, val = call_method(methodName, *params)
244                if ok
245                  # correct return value
246                  [val]
247                else
248                  # exception
249                  multicall_fault(val.faultCode, val.faultString) 
250                end
251              end
252            end
253          end  
254           
255        else
256          multicall_fault(ERR_MC_EXPECTED_STRUCT, "system.multicall expected struct")
257        end
258      } 
259    end # end add_handler
260    self
261  end
262
263  def add_introspection
264    add_handler("system.listMethods",%w(array), "List methods available on this XML-RPC server") do
265      methods = []
266      @handler.each do |name, obj|
267        if obj.kind_of? Proc
268          methods << name
269        else
270          obj.class.public_instance_methods(false).each do |meth|
271            methods << "#{name}#{meth}"
272          end
273        end
274      end
275      methods
276    end
277
278    add_handler("system.methodSignature", %w(array string), "Returns method signature") do |meth|
279      sigs = []
280      @handler.each do |name, obj, sig|
281        if obj.kind_of? Proc and sig != nil and name == meth
282          if sig[0].kind_of? Array
283            # sig contains multiple signatures, e.g. [["array"], ["array", "string"]]
284            sig.each {|s| sigs << s}
285          else
286            # sig is a single signature, e.g. ["array"]
287            sigs << sig 
288          end
289        end
290      end
291      sigs.uniq! || sigs  # remove eventually duplicated signatures
292    end
293
294    add_handler("system.methodHelp", %w(string string), "Returns help on using this method") do |meth|
295      help = nil 
296      @handler.each do |name, obj, sig, hlp|
297        if obj.kind_of? Proc and name == meth 
298          help = hlp
299          break      
300        end
301      end
302      help || ""
303    end
304
305    self
306  end
307
308
309  
310  def process(data)
311    method, params = parser().parseMethodCall(data) 
312    handle(method, *params)
313  end
314 
315  private # --------------------------------------------------------------
316
317  def multicall_fault(nr, str)
318    {"faultCode" => nr, "faultString" => str}
319  end
320 
321  #
322  # method dispatch
323  #
324  def dispatch(methodname, *args)
325    for name, obj in @handler
326      if obj.kind_of? Proc
327        next unless methodname == name
328      else
329        next unless methodname =~ /^#{name}(.+)$/
330        next unless obj.respond_to? $1
331        obj = obj.method($1)
332      end
333
334      if check_arity(obj, args.size)
335        if @service_hook.nil?
336          return obj.call(*args) 
337        else
338          return @service_hook.call(obj, *args)
339        end
340      end
341    end 
342 
343    if @default_handler.nil?
344      raise XMLRPC::FaultException.new(ERR_METHOD_MISSING, "Method #{methodname} missing or wrong number of parameters!")
345    else
346      @default_handler.call(methodname, *args) 
347    end
348  end
349
350
351  #
352  # returns true, if the arity of "obj" matches
353  #
354  def check_arity(obj, n_args)
355    ary = obj.arity
356
357    if ary >= 0
358      n_args == ary
359    else
360      n_args >= (ary+1).abs 
361    end
362  end
363
364
365
366  def call_method(methodname, *args)
367    begin
368      [true, dispatch(methodname, *args)]
369    rescue XMLRPC::FaultException => e  
370      [false, e]  
371    rescue Exception => e
372      [false, XMLRPC::FaultException.new(ERR_UNCAUGHT_EXCEPTION, "Uncaught exception #{e.message} in method #{methodname}")]
373    end
374  end
375
376  #
377  #
378  #
379  def handle(methodname, *args)
380    create().methodResponse(*call_method(methodname, *args))
381  end
382
383
384end
385
386
387=begin
388= XMLRPC::CGIServer
389== Synopsis
390    require "xmlrpc/server"
391 
392    s = XMLRPC::CGIServer.new     
393
394    s.add_handler("michael.add") do |a,b|
395      a + b
396    end
397
398    s.add_handler("michael.div") do |a,b|
399      if b == 0
400        raise XMLRPC::FaultException.new(1, "division by zero")
401      else
402        a / b 
403      end
404    end 
405
406    s.set_default_handler do |name, *args|
407      raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
408                                       " or wrong number of parameters!")
409    end
410  
411    s.serve
412
413== Description
414Implements a CGI-based XML-RPC server.
415
416== Superclass
417((<XMLRPC::BasicServer>))
418
419== Class Methods
420--- XMLRPC::CGIServer.new( *a )
421    Creates a new (({XMLRPC::CGIServer})) instance. All parameters given
422    are by-passed to ((<XMLRPC::BasicServer.new>)). You can only create 
423    ((*one*)) (({XMLRPC::CGIServer})) instance, because more than one makes
424    no sense.
425
426== Instance Methods
427--- XMLRPC::CGIServer#serve
428    Call this after you have added all you handlers to the server.
429    This method processes a XML-RPC methodCall and sends the answer
430    back to the client. 
431    Make sure that you don't write to standard-output in a handler, or in
432    any other part of your program, this would case a CGI-based server to fail!
433=end
434
435class CGIServer < BasicServer
436  @@obj = nil
437
438  def CGIServer.new(*a)
439    @@obj = super(*a) if @@obj.nil?
440    @@obj
441  end
442
443  def initialize(*a)
444    super(*a)
445  end
446  
447  def serve
448    catch(:exit_serve) {
449      length = ENV['CONTENT_LENGTH'].to_i
450
451      http_error(405, "Method Not Allowed") unless ENV['REQUEST_METHOD'] == "POST" 
452      http_error(400, "Bad Request")        unless parse_content_type(ENV['CONTENT_TYPE']).first == "text/xml"
453      http_error(411, "Length Required")    unless length > 0 
454
455      # TODO: do we need a call to binmode?
456      $stdin.binmode if $stdin.respond_to? :binmode
457      data = $stdin.read(length)
458
459      http_error(400, "Bad Request")        if data.nil? or data.size != length
460
461      http_write(process(data), "Content-type" => "text/xml; charset=utf-8")
462    }
463  end
464
465
466  private
467
468  def http_error(status, message)
469    err = "#{status} #{message}"
470    msg = <<-"MSGEND" 
471      <html>
472        <head>
473          <title>#{err}</title>
474        </head>
475        <body>
476          <h1>#{err}</h1>
477          <p>Unexpected error occured while processing XML-RPC request!</p>
478        </body>
479      </html>
480    MSGEND
481
482    http_write(msg, "Status" => err, "Content-type" => "text/html")
483    throw :exit_serve # exit from the #serve method
484  end
485
486  def http_write(body, header)
487    h = {}
488    header.each {|key, value| h[key.to_s.capitalize] = value}
489    h['Status']         ||= "200 OK"
490    h['Content-length'] ||= body.size.to_s 
491
492    str = ""
493    h.each {|key, value| str << "#{key}: #{value}\r\n"}
494    str << "\r\n#{body}"
495
496    print str
497  end
498
499end
500
501=begin
502= XMLRPC::ModRubyServer
503== Description
504Implements a XML-RPC server, which works with Apache mod_ruby.
505
506Use it in the same way as CGIServer!
507
508== Superclass
509((<XMLRPC::BasicServer>))
510=end 
511
512class ModRubyServer < BasicServer
513
514  def initialize(*a)
515    @ap = Apache::request
516    super(*a)
517  end
518
519  def serve
520    catch(:exit_serve) {
521      header = {}
522      @ap.headers_in.each {|key, value| header[key.capitalize] = value}
523
524      length = header['Content-length'].to_i
525
526      http_error(405, "Method Not Allowed") unless @ap.request_method  == "POST" 
527      http_error(400, "Bad Request")        unless parse_content_type(header['Content-type']).first == "text/xml"
528      http_error(411, "Length Required")    unless length > 0 
529
530      # TODO: do we need a call to binmode?
531      @ap.binmode
532      data = @ap.read(length)
533
534      http_error(400, "Bad Request")        if data.nil? or data.size != length
535
536      http_write(process(data), 200, "Content-type" => "text/xml; charset=utf-8")
537    }
538  end
539
540
541  private
542
543  def http_error(status, message)
544    err = "#{status} #{message}"
545    msg = <<-"MSGEND" 
546      <html>
547        <head>
548          <title>#{err}</title>
549        </head>
550        <body>
551          <h1>#{err}</h1>
552          <p>Unexpected error occured while processing XML-RPC request!</p>
553        </body>
554      </html>
555    MSGEND
556
557    http_write(msg, status, "Status" => err, "Content-type" => "text/html")
558    throw :exit_serve # exit from the #serve method
559  end
560
561  def http_write(body, status, header)
562    h = {}
563    header.each {|key, value| h[key.to_s.capitalize] = value}
564    h['Status']         ||= "200 OK"
565    h['Content-length'] ||= body.size.to_s 
566
567    h.each {|key, value| @ap.headers_out[key] = value }
568    @ap.content_type = h["Content-type"] 
569    @ap.status = status.to_i 
570    @ap.send_http_header 
571
572    @ap.print body
573  end
574
575end
576
577=begin
578= XMLRPC::Server
579== Synopsis
580    require "xmlrpc/server"
581 
582    s = XMLRPC::Server.new(8080) 
583
584    s.add_handler("michael.add") do |a,b|
585      a + b
586    end
587
588    s.add_handler("michael.div") do |a,b|
589      if b == 0
590        raise XMLRPC::FaultException.new(1, "division by zero")
591      else
592        a / b 
593      end
594    end 
595
596    s.set_default_handler do |name, *args|
597      raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
598                                       " or wrong number of parameters!")
599    end
600 
601    s.serve
602
603== Description
604Implements a standalone XML-RPC server. The method (({serve}))) is left if a SIGHUP is sent to the
605program.
606
607== Superclass
608((<XMLRPC::WEBrickServlet>))
609
610== Class Methods
611--- XMLRPC::Server.new( port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a )
612    Creates a new (({XMLRPC::Server})) instance, which is a XML-RPC server listening on
613    port ((|port|)) and accepts requests for the host ((|host|)), which is by default only the localhost. 
614    The server is not started, to start it you have to call ((<serve|XMLRPC::Server#serve>)).
615
616    Parameters ((|audit|)) and ((|debug|)) are obsolete!
617
618    All additionally given parameters in ((|*a|)) are by-passed to ((<XMLRPC::BasicServer.new>)). 
619    
620== Instance Methods
621--- XMLRPC::Server#serve
622    Call this after you have added all you handlers to the server.
623    This method starts the server to listen for XML-RPC requests and answer them.
624
625--- XMLRPC::Server#shutdown
626    Stops and shuts the server down.
627    
628=end
629
630class WEBrickServlet < BasicServer; end # forward declaration
631
632class Server < WEBrickServlet
633
634  def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a)
635    super(*a)
636    require 'webrick'
637    @server = WEBrick::HTTPServer.new(:Port => port, :BindAddress => host, :MaxClients => maxConnections, 
638                                      :Logger => WEBrick::Log.new(stdlog))
639    @server.mount("/", self)
640  end
641  
642  def serve
643    if RUBY_PLATFORM =~ /mingw|mswin32/
644      signals = [1]
645    else
646      signals = %w[INT TERM HUP]
647    end
648    signals.each { |signal| trap(signal) { @server.shutdown } }
649
650    @server.start
651  end
652  
653  def shutdown
654    @server.shutdown
655  end
656 
657end
658
659=begin
660= XMLRPC::WEBrickServlet
661== Synopsis
662
663    require "webrick"
664    require "xmlrpc/server"
665
666    s = XMLRPC::WEBrickServlet.new
667    s.add_handler("michael.add") do |a,b|
668      a + b
669    end
670
671    s.add_handler("michael.div") do |a,b|
672      if b == 0
673        raise XMLRPC::FaultException.new(1, "division by zero")
674      else
675        a / b 
676      end
677    end 
678
679    s.set_default_handler do |name, *args|
680      raise XMLRPC::FaultException.new(-99, "Method #{name} missing" +
681                                       " or wrong number of parameters!")
682    end
683
684    httpserver = WEBrick::HTTPServer.new(:Port => 8080)    
685    httpserver.mount("/RPC2", s)
686    trap("HUP") { httpserver.shutdown }   # use 1 instead of "HUP" on Windows
687    httpserver.start
688
689== Instance Methods
690
691--- XMLRPC::WEBrickServlet#set_valid_ip( *ip_addr )
692    Specifies the valid IP addresses that are allowed to connect to the server.
693    Each IP is either a (({String})) or a (({Regexp})).
694
695--- XMLRPC::WEBrickServlet#get_valid_ip
696    Return the via method ((<set_valid_ip|XMLRPC::Server#set_valid_ip>)) specified
697    valid IP addresses.
698 
699== Description
700Implements a servlet for use with WEBrick, a pure Ruby (HTTP-) server framework.
701
702== Superclass
703((<XMLRPC::BasicServer>))
704
705=end
706
707class WEBrickServlet < BasicServer
708  def initialize(*a)
709    super
710    require "webrick/httpstatus"
711    @valid_ip = nil
712  end
713
714  # deprecated from WEBrick/1.2.2. 
715  # but does not break anything.
716  def require_path_info?
717    false 
718  end
719
720  def get_instance(config, *options)
721    # TODO: set config & options
722    self
723  end
724
725  def set_valid_ip(*ip_addr)
726    if ip_addr.size == 1 and ip_addr[0].nil?
727      @valid_ip = nil
728    else
729      @valid_ip = ip_addr
730    end
731  end
732
733  def get_valid_ip
734    @valid_ip
735  end
736
737  def service(request, response)
738
739    if @valid_ip 
740      raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip }
741    end
742
743    if request.request_method != "POST"
744      raise WEBrick::HTTPStatus::MethodNotAllowed,
745            "unsupported method `#{request.request_method}'."
746    end
747
748    if parse_content_type(request['Content-type']).first != "text/xml" 
749      raise WEBrick::HTTPStatus::BadRequest
750    end 
751
752    length = (request['Content-length'] || 0).to_i
753
754    raise WEBrick::HTTPStatus::LengthRequired unless length > 0
755
756    data = request.body
757
758    if data.nil? or data.size != length
759      raise WEBrick::HTTPStatus::BadRequest
760    end
761
762    resp = process(data)
763    if resp.nil? or resp.size <= 0  
764      raise WEBrick::HTTPStatus::InternalServerError
765    end
766
767    response.status = 200
768    response['Content-Length'] = resp.size
769    response['Content-Type']   = "text/xml; charset=utf-8"
770    response.body = resp 
771  end
772end
773
774
775end # module XMLRPC
776
777
778=begin
779= History
780    $Id: server.rb 22461 2009-02-20 09:06:53Z shyouhei $    
781=end
782