rubysl-xmlrpc 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+