xmlrpc 0.1.0

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