rubysl-xmlrpc 1.0.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,782 @@
1
+ =begin
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
+ = Classes
8
+ * ((<XMLRPC::BasicServer>))
9
+ * ((<XMLRPC::CGIServer>))
10
+ * ((<XMLRPC::ModRubyServer>))
11
+ * ((<XMLRPC::Server>))
12
+ * ((<XMLRPC::WEBrickServlet>))
13
+
14
+ = XMLRPC::BasicServer
15
+ == Description
16
+ Is the base class for all XML-RPC server-types (CGI, standalone).
17
+ You can add handler and set a default handler.
18
+ Do not use this server, as this is/should be an abstract class.
19
+
20
+ === How the method to call is found
21
+ The arity (number of accepted arguments) of a handler (method or (({Proc})) object) is
22
+ compared to the given arguments submitted by the client for a RPC ((-Remote Procedure Call-)).
23
+ A handler is only called if it accepts the number of arguments, otherwise the search
24
+ for another handler will go on. When at the end no handler was found,
25
+ the ((<default_handler|XMLRPC::BasicServer#set_default_handler>)) will be called.
26
+ With this technique it is possible to do overloading by number of parameters, but
27
+ only for (({Proc})) handler, because you cannot define two methods of the same name in
28
+ the 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
+
145
+ require "xmlrpc/parser"
146
+ require "xmlrpc/create"
147
+ require "xmlrpc/config"
148
+ require "xmlrpc/utils" # ParserWriterChooseMixin
149
+
150
+
151
+
152
+ module XMLRPC
153
+
154
+
155
+ class 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
+
384
+ end
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
414
+ Implements 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
+
435
+ class 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
+
499
+ end
500
+
501
+ =begin
502
+ = XMLRPC::ModRubyServer
503
+ == Description
504
+ Implements a XML-RPC server, which works with Apache mod_ruby.
505
+
506
+ Use it in the same way as CGIServer!
507
+
508
+ == Superclass
509
+ ((<XMLRPC::BasicServer>))
510
+ =end
511
+
512
+ class 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
+
575
+ end
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
604
+ Implements a standalone XML-RPC server. The method (({serve}))) is left if a SIGHUP is sent to the
605
+ program.
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
+
630
+ class WEBrickServlet < BasicServer; end # forward declaration
631
+
632
+ class 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
+
657
+ end
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
700
+ Implements a servlet for use with WEBrick, a pure Ruby (HTTP-) server framework.
701
+
702
+ == Superclass
703
+ ((<XMLRPC::BasicServer>))
704
+
705
+ =end
706
+
707
+ class 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
772
+ end
773
+
774
+
775
+ end # module XMLRPC
776
+
777
+
778
+ =begin
779
+ = History
780
+ $Id: server.rb 22461 2009-02-20 09:06:53Z shyouhei $
781
+ =end
782
+