xmlrpc 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+