drb 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1937 @@
1
+ # frozen_string_literal: false
2
+ #
3
+ # = drb/drb.rb
4
+ #
5
+ # Distributed Ruby: _dRuby_ version 2.0.4
6
+ #
7
+ # Copyright (c) 1999-2003 Masatoshi SEKI. You can redistribute it and/or
8
+ # modify it under the same terms as Ruby.
9
+ #
10
+ # Author:: Masatoshi SEKI
11
+ #
12
+ # Documentation:: William Webber (william@williamwebber.com)
13
+ #
14
+ # == Overview
15
+ #
16
+ # dRuby is a distributed object system for Ruby. It allows an object in one
17
+ # Ruby process to invoke methods on an object in another Ruby process on the
18
+ # same or a different machine.
19
+ #
20
+ # The Ruby standard library contains the core classes of the dRuby package.
21
+ # However, the full package also includes access control lists and the
22
+ # Rinda tuple-space distributed task management system, as well as a
23
+ # large number of samples. The full dRuby package can be downloaded from
24
+ # the dRuby home page (see *References*).
25
+ #
26
+ # For an introduction and examples of usage see the documentation to the
27
+ # DRb module.
28
+ #
29
+ # == References
30
+ #
31
+ # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.html]
32
+ # The dRuby home page, in Japanese. Contains the full dRuby package
33
+ # and links to other Japanese-language sources.
34
+ #
35
+ # [http://www2a.biglobe.ne.jp/~seki/ruby/druby.en.html]
36
+ # The English version of the dRuby home page.
37
+ #
38
+ # [http://pragprog.com/book/sidruby/the-druby-book]
39
+ # The dRuby Book: Distributed and Parallel Computing with Ruby
40
+ # by Masatoshi Seki and Makoto Inoue
41
+ #
42
+ # [http://www.ruby-doc.org/docs/ProgrammingRuby/html/ospace.html]
43
+ # The chapter from *Programming* *Ruby* by Dave Thomas and Andy Hunt
44
+ # which discusses dRuby.
45
+ #
46
+ # [http://www.clio.ne.jp/home/web-i31s/Flotuard/Ruby/PRC2K_seki/dRuby.en.html]
47
+ # Translation of presentation on Ruby by Masatoshi Seki.
48
+
49
+ require 'socket'
50
+ require 'io/wait'
51
+ require 'monitor'
52
+ require_relative 'eq'
53
+
54
+ #
55
+ # == Overview
56
+ #
57
+ # dRuby is a distributed object system for Ruby. It is written in
58
+ # pure Ruby and uses its own protocol. No add-in services are needed
59
+ # beyond those provided by the Ruby runtime, such as TCP sockets. It
60
+ # does not rely on or interoperate with other distributed object
61
+ # systems such as CORBA, RMI, or .NET.
62
+ #
63
+ # dRuby allows methods to be called in one Ruby process upon a Ruby
64
+ # object located in another Ruby process, even on another machine.
65
+ # References to objects can be passed between processes. Method
66
+ # arguments and return values are dumped and loaded in marshalled
67
+ # format. All of this is done transparently to both the caller of the
68
+ # remote method and the object that it is called upon.
69
+ #
70
+ # An object in a remote process is locally represented by a
71
+ # DRb::DRbObject instance. This acts as a sort of proxy for the
72
+ # remote object. Methods called upon this DRbObject instance are
73
+ # forwarded to its remote object. This is arranged dynamically at run
74
+ # time. There are no statically declared interfaces for remote
75
+ # objects, such as CORBA's IDL.
76
+ #
77
+ # dRuby calls made into a process are handled by a DRb::DRbServer
78
+ # instance within that process. This reconstitutes the method call,
79
+ # invokes it upon the specified local object, and returns the value to
80
+ # the remote caller. Any object can receive calls over dRuby. There
81
+ # is no need to implement a special interface, or mixin special
82
+ # functionality. Nor, in the general case, does an object need to
83
+ # explicitly register itself with a DRbServer in order to receive
84
+ # dRuby calls.
85
+ #
86
+ # One process wishing to make dRuby calls upon another process must
87
+ # somehow obtain an initial reference to an object in the remote
88
+ # process by some means other than as the return value of a remote
89
+ # method call, as there is initially no remote object reference it can
90
+ # invoke a method upon. This is done by attaching to the server by
91
+ # URI. Each DRbServer binds itself to a URI such as
92
+ # 'druby://example.com:8787'. A DRbServer can have an object attached
93
+ # to it that acts as the server's *front* *object*. A DRbObject can
94
+ # be explicitly created from the server's URI. This DRbObject's
95
+ # remote object will be the server's front object. This front object
96
+ # can then return references to other Ruby objects in the DRbServer's
97
+ # process.
98
+ #
99
+ # Method calls made over dRuby behave largely the same as normal Ruby
100
+ # method calls made within a process. Method calls with blocks are
101
+ # supported, as are raising exceptions. In addition to a method's
102
+ # standard errors, a dRuby call may also raise one of the
103
+ # dRuby-specific errors, all of which are subclasses of DRb::DRbError.
104
+ #
105
+ # Any type of object can be passed as an argument to a dRuby call or
106
+ # returned as its return value. By default, such objects are dumped
107
+ # or marshalled at the local end, then loaded or unmarshalled at the
108
+ # remote end. The remote end therefore receives a copy of the local
109
+ # object, not a distributed reference to it; methods invoked upon this
110
+ # copy are executed entirely in the remote process, not passed on to
111
+ # the local original. This has semantics similar to pass-by-value.
112
+ #
113
+ # However, if an object cannot be marshalled, a dRuby reference to it
114
+ # is passed or returned instead. This will turn up at the remote end
115
+ # as a DRbObject instance. All methods invoked upon this remote proxy
116
+ # are forwarded to the local object, as described in the discussion of
117
+ # DRbObjects. This has semantics similar to the normal Ruby
118
+ # pass-by-reference.
119
+ #
120
+ # The easiest way to signal that we want an otherwise marshallable
121
+ # object to be passed or returned as a DRbObject reference, rather
122
+ # than marshalled and sent as a copy, is to include the
123
+ # DRb::DRbUndumped mixin module.
124
+ #
125
+ # dRuby supports calling remote methods with blocks. As blocks (or
126
+ # rather the Proc objects that represent them) are not marshallable,
127
+ # the block executes in the local, not the remote, context. Each
128
+ # value yielded to the block is passed from the remote object to the
129
+ # local block, then the value returned by each block invocation is
130
+ # passed back to the remote execution context to be collected, before
131
+ # the collected values are finally returned to the local context as
132
+ # the return value of the method invocation.
133
+ #
134
+ # == Examples of usage
135
+ #
136
+ # For more dRuby samples, see the +samples+ directory in the full
137
+ # dRuby distribution.
138
+ #
139
+ # === dRuby in client/server mode
140
+ #
141
+ # This illustrates setting up a simple client-server drb
142
+ # system. Run the server and client code in different terminals,
143
+ # starting the server code first.
144
+ #
145
+ # ==== Server code
146
+ #
147
+ # require 'drb/drb'
148
+ #
149
+ # # The URI for the server to connect to
150
+ # URI="druby://localhost:8787"
151
+ #
152
+ # class TimeServer
153
+ #
154
+ # def get_current_time
155
+ # return Time.now
156
+ # end
157
+ #
158
+ # end
159
+ #
160
+ # # The object that handles requests on the server
161
+ # FRONT_OBJECT=TimeServer.new
162
+ #
163
+ # DRb.start_service(URI, FRONT_OBJECT)
164
+ # # Wait for the drb server thread to finish before exiting.
165
+ # DRb.thread.join
166
+ #
167
+ # ==== Client code
168
+ #
169
+ # require 'drb/drb'
170
+ #
171
+ # # The URI to connect to
172
+ # SERVER_URI="druby://localhost:8787"
173
+ #
174
+ # # Start a local DRbServer to handle callbacks.
175
+ # #
176
+ # # Not necessary for this small example, but will be required
177
+ # # as soon as we pass a non-marshallable object as an argument
178
+ # # to a dRuby call.
179
+ # #
180
+ # # Note: this must be called at least once per process to take any effect.
181
+ # # This is particularly important if your application forks.
182
+ # DRb.start_service
183
+ #
184
+ # timeserver = DRbObject.new_with_uri(SERVER_URI)
185
+ # puts timeserver.get_current_time
186
+ #
187
+ # === Remote objects under dRuby
188
+ #
189
+ # This example illustrates returning a reference to an object
190
+ # from a dRuby call. The Logger instances live in the server
191
+ # process. References to them are returned to the client process,
192
+ # where methods can be invoked upon them. These methods are
193
+ # executed in the server process.
194
+ #
195
+ # ==== Server code
196
+ #
197
+ # require 'drb/drb'
198
+ #
199
+ # URI="druby://localhost:8787"
200
+ #
201
+ # class Logger
202
+ #
203
+ # # Make dRuby send Logger instances as dRuby references,
204
+ # # not copies.
205
+ # include DRb::DRbUndumped
206
+ #
207
+ # def initialize(n, fname)
208
+ # @name = n
209
+ # @filename = fname
210
+ # end
211
+ #
212
+ # def log(message)
213
+ # File.open(@filename, "a") do |f|
214
+ # f.puts("#{Time.now}: #{@name}: #{message}")
215
+ # end
216
+ # end
217
+ #
218
+ # end
219
+ #
220
+ # # We have a central object for creating and retrieving loggers.
221
+ # # This retains a local reference to all loggers created. This
222
+ # # is so an existing logger can be looked up by name, but also
223
+ # # to prevent loggers from being garbage collected. A dRuby
224
+ # # reference to an object is not sufficient to prevent it being
225
+ # # garbage collected!
226
+ # class LoggerFactory
227
+ #
228
+ # def initialize(bdir)
229
+ # @basedir = bdir
230
+ # @loggers = {}
231
+ # end
232
+ #
233
+ # def get_logger(name)
234
+ # if !@loggers.has_key? name
235
+ # # make the filename safe, then declare it to be so
236
+ # fname = name.gsub(/[.\/\\\:]/, "_")
237
+ # @loggers[name] = Logger.new(name, @basedir + "/" + fname)
238
+ # end
239
+ # return @loggers[name]
240
+ # end
241
+ #
242
+ # end
243
+ #
244
+ # FRONT_OBJECT=LoggerFactory.new("/tmp/dlog")
245
+ #
246
+ # DRb.start_service(URI, FRONT_OBJECT)
247
+ # DRb.thread.join
248
+ #
249
+ # ==== Client code
250
+ #
251
+ # require 'drb/drb'
252
+ #
253
+ # SERVER_URI="druby://localhost:8787"
254
+ #
255
+ # DRb.start_service
256
+ #
257
+ # log_service=DRbObject.new_with_uri(SERVER_URI)
258
+ #
259
+ # ["loga", "logb", "logc"].each do |logname|
260
+ #
261
+ # logger=log_service.get_logger(logname)
262
+ #
263
+ # logger.log("Hello, world!")
264
+ # logger.log("Goodbye, world!")
265
+ # logger.log("=== EOT ===")
266
+ #
267
+ # end
268
+ #
269
+ # == Security
270
+ #
271
+ # As with all network services, security needs to be considered when
272
+ # using dRuby. By allowing external access to a Ruby object, you are
273
+ # not only allowing outside clients to call the methods you have
274
+ # defined for that object, but by default to execute arbitrary Ruby
275
+ # code on your server. Consider the following:
276
+ #
277
+ # # !!! UNSAFE CODE !!!
278
+ # ro = DRbObject::new_with_uri("druby://your.server.com:8989")
279
+ # class << ro
280
+ # undef :instance_eval # force call to be passed to remote object
281
+ # end
282
+ # ro.instance_eval("`rm -rf *`")
283
+ #
284
+ # The dangers posed by instance_eval and friends are such that a
285
+ # DRbServer should only be used when clients are trusted.
286
+ #
287
+ # A DRbServer can be configured with an access control list to
288
+ # selectively allow or deny access from specified IP addresses. The
289
+ # main druby distribution provides the ACL class for this purpose. In
290
+ # general, this mechanism should only be used alongside, rather than
291
+ # as a replacement for, a good firewall.
292
+ #
293
+ # == dRuby internals
294
+ #
295
+ # dRuby is implemented using three main components: a remote method
296
+ # call marshaller/unmarshaller; a transport protocol; and an
297
+ # ID-to-object mapper. The latter two can be directly, and the first
298
+ # indirectly, replaced, in order to provide different behaviour and
299
+ # capabilities.
300
+ #
301
+ # Marshalling and unmarshalling of remote method calls is performed by
302
+ # a DRb::DRbMessage instance. This uses the Marshal module to dump
303
+ # the method call before sending it over the transport layer, then
304
+ # reconstitute it at the other end. There is normally no need to
305
+ # replace this component, and no direct way is provided to do so.
306
+ # However, it is possible to implement an alternative marshalling
307
+ # scheme as part of an implementation of the transport layer.
308
+ #
309
+ # The transport layer is responsible for opening client and server
310
+ # network connections and forwarding dRuby request across them.
311
+ # Normally, it uses DRb::DRbMessage internally to manage marshalling
312
+ # and unmarshalling. The transport layer is managed by
313
+ # DRb::DRbProtocol. Multiple protocols can be installed in
314
+ # DRbProtocol at the one time; selection between them is determined by
315
+ # the scheme of a dRuby URI. The default transport protocol is
316
+ # selected by the scheme 'druby:', and implemented by
317
+ # DRb::DRbTCPSocket. This uses plain TCP/IP sockets for
318
+ # communication. An alternative protocol, using UNIX domain sockets,
319
+ # is implemented by DRb::DRbUNIXSocket in the file drb/unix.rb, and
320
+ # selected by the scheme 'drbunix:'. A sample implementation over
321
+ # HTTP can be found in the samples accompanying the main dRuby
322
+ # distribution.
323
+ #
324
+ # The ID-to-object mapping component maps dRuby object ids to the
325
+ # objects they refer to, and vice versa. The implementation to use
326
+ # can be specified as part of a DRb::DRbServer's configuration. The
327
+ # default implementation is provided by DRb::DRbIdConv. It uses an
328
+ # object's ObjectSpace id as its dRuby id. This means that the dRuby
329
+ # reference to that object only remains meaningful for the lifetime of
330
+ # the object's process and the lifetime of the object within that
331
+ # process. A modified implementation is provided by DRb::TimerIdConv
332
+ # in the file drb/timeridconv.rb. This implementation retains a local
333
+ # reference to all objects exported over dRuby for a configurable
334
+ # period of time (defaulting to ten minutes), to prevent them being
335
+ # garbage-collected within this time. Another sample implementation
336
+ # is provided in sample/name.rb in the main dRuby distribution. This
337
+ # allows objects to specify their own id or "name". A dRuby reference
338
+ # can be made persistent across processes by having each process
339
+ # register an object using the same dRuby name.
340
+ #
341
+ module DRb
342
+
343
+ # Superclass of all errors raised in the DRb module.
344
+ class DRbError < RuntimeError; end
345
+
346
+ # Error raised when an error occurs on the underlying communication
347
+ # protocol.
348
+ class DRbConnError < DRbError; end
349
+
350
+ # Class responsible for converting between an object and its id.
351
+ #
352
+ # This, the default implementation, uses an object's local ObjectSpace
353
+ # __id__ as its id. This means that an object's identification over
354
+ # drb remains valid only while that object instance remains alive
355
+ # within the server runtime.
356
+ #
357
+ # For alternative mechanisms, see DRb::TimerIdConv in drb/timeridconv.rb
358
+ # and DRbNameIdConv in sample/name.rb in the full drb distribution.
359
+ class DRbIdConv
360
+
361
+ # Convert an object reference id to an object.
362
+ #
363
+ # This implementation looks up the reference id in the local object
364
+ # space and returns the object it refers to.
365
+ def to_obj(ref)
366
+ ObjectSpace._id2ref(ref)
367
+ end
368
+
369
+ # Convert an object into a reference id.
370
+ #
371
+ # This implementation returns the object's __id__ in the local
372
+ # object space.
373
+ def to_id(obj)
374
+ case obj
375
+ when Object
376
+ obj.nil? ? nil : obj.__id__
377
+ when BasicObject
378
+ obj.__id__
379
+ end
380
+ end
381
+ end
382
+
383
+ # Mixin module making an object undumpable or unmarshallable.
384
+ #
385
+ # If an object which includes this module is returned by method
386
+ # called over drb, then the object remains in the server space
387
+ # and a reference to the object is returned, rather than the
388
+ # object being marshalled and moved into the client space.
389
+ module DRbUndumped
390
+ def _dump(dummy) # :nodoc:
391
+ raise TypeError, 'can\'t dump'
392
+ end
393
+ end
394
+
395
+ # Error raised by the DRb module when an attempt is made to refer to
396
+ # the context's current drb server but the context does not have one.
397
+ # See #current_server.
398
+ class DRbServerNotFound < DRbError; end
399
+
400
+ # Error raised by the DRbProtocol module when it cannot find any
401
+ # protocol implementation support the scheme specified in a URI.
402
+ class DRbBadURI < DRbError; end
403
+
404
+ # Error raised by a dRuby protocol when it doesn't support the
405
+ # scheme specified in a URI. See DRb::DRbProtocol.
406
+ class DRbBadScheme < DRbError; end
407
+
408
+ # An exception wrapping a DRb::DRbUnknown object
409
+ class DRbUnknownError < DRbError
410
+
411
+ # Create a new DRbUnknownError for the DRb::DRbUnknown object +unknown+
412
+ def initialize(unknown)
413
+ @unknown = unknown
414
+ super(unknown.name)
415
+ end
416
+
417
+ # Get the wrapped DRb::DRbUnknown object.
418
+ attr_reader :unknown
419
+
420
+ def self._load(s) # :nodoc:
421
+ Marshal::load(s)
422
+ end
423
+
424
+ def _dump(lv) # :nodoc:
425
+ Marshal::dump(@unknown)
426
+ end
427
+ end
428
+
429
+ # An exception wrapping an error object
430
+ class DRbRemoteError < DRbError
431
+
432
+ # Creates a new remote error that wraps the Exception +error+
433
+ def initialize(error)
434
+ @reason = error.class.to_s
435
+ super("#{error.message} (#{error.class})")
436
+ set_backtrace(error.backtrace)
437
+ end
438
+
439
+ # the class of the error, as a string.
440
+ attr_reader :reason
441
+ end
442
+
443
+ # Class wrapping a marshalled object whose type is unknown locally.
444
+ #
445
+ # If an object is returned by a method invoked over drb, but the
446
+ # class of the object is unknown in the client namespace, or
447
+ # the object is a constant unknown in the client namespace, then
448
+ # the still-marshalled object is returned wrapped in a DRbUnknown instance.
449
+ #
450
+ # If this object is passed as an argument to a method invoked over
451
+ # drb, then the wrapped object is passed instead.
452
+ #
453
+ # The class or constant name of the object can be read from the
454
+ # +name+ attribute. The marshalled object is held in the +buf+
455
+ # attribute.
456
+ class DRbUnknown
457
+
458
+ # Create a new DRbUnknown object.
459
+ #
460
+ # +buf+ is a string containing a marshalled object that could not
461
+ # be unmarshalled. +err+ is the error message that was raised
462
+ # when the unmarshalling failed. It is used to determine the
463
+ # name of the unmarshalled object.
464
+ def initialize(err, buf)
465
+ case err.to_s
466
+ when /uninitialized constant (\S+)/
467
+ @name = $1
468
+ when /undefined class\/module (\S+)/
469
+ @name = $1
470
+ else
471
+ @name = nil
472
+ end
473
+ @buf = buf
474
+ end
475
+
476
+ # The name of the unknown thing.
477
+ #
478
+ # Class name for unknown objects; variable name for unknown
479
+ # constants.
480
+ attr_reader :name
481
+
482
+ # Buffer contained the marshalled, unknown object.
483
+ attr_reader :buf
484
+
485
+ def self._load(s) # :nodoc:
486
+ begin
487
+ Marshal::load(s)
488
+ rescue NameError, ArgumentError
489
+ DRbUnknown.new($!, s)
490
+ end
491
+ end
492
+
493
+ def _dump(lv) # :nodoc:
494
+ @buf
495
+ end
496
+
497
+ # Attempt to load the wrapped marshalled object again.
498
+ #
499
+ # If the class of the object is now known locally, the object
500
+ # will be unmarshalled and returned. Otherwise, a new
501
+ # but identical DRbUnknown object will be returned.
502
+ def reload
503
+ self.class._load(@buf)
504
+ end
505
+
506
+ # Create a DRbUnknownError exception containing this object.
507
+ def exception
508
+ DRbUnknownError.new(self)
509
+ end
510
+ end
511
+
512
+ # An Array wrapper that can be sent to another server via DRb.
513
+ #
514
+ # All entries in the array will be dumped or be references that point to
515
+ # the local server.
516
+
517
+ class DRbArray
518
+
519
+ # Creates a new DRbArray that either dumps or wraps all the items in the
520
+ # Array +ary+ so they can be loaded by a remote DRb server.
521
+
522
+ def initialize(ary)
523
+ @ary = ary.collect { |obj|
524
+ if obj.kind_of? DRbUndumped
525
+ DRbObject.new(obj)
526
+ else
527
+ begin
528
+ Marshal.dump(obj)
529
+ obj
530
+ rescue
531
+ DRbObject.new(obj)
532
+ end
533
+ end
534
+ }
535
+ end
536
+
537
+ def self._load(s) # :nodoc:
538
+ Marshal::load(s)
539
+ end
540
+
541
+ def _dump(lv) # :nodoc:
542
+ Marshal.dump(@ary)
543
+ end
544
+ end
545
+
546
+ # Handler for sending and receiving drb messages.
547
+ #
548
+ # This takes care of the low-level marshalling and unmarshalling
549
+ # of drb requests and responses sent over the wire between server
550
+ # and client. This relieves the implementor of a new drb
551
+ # protocol layer with having to deal with these details.
552
+ #
553
+ # The user does not have to directly deal with this object in
554
+ # normal use.
555
+ class DRbMessage
556
+ def initialize(config) # :nodoc:
557
+ @load_limit = config[:load_limit]
558
+ @argc_limit = config[:argc_limit]
559
+ end
560
+
561
+ def dump(obj, error=false) # :nodoc:
562
+ case obj
563
+ when DRbUndumped
564
+ obj = make_proxy(obj, error)
565
+ when Object
566
+ # nothing
567
+ else
568
+ obj = make_proxy(obj, error)
569
+ end
570
+ begin
571
+ str = Marshal::dump(obj)
572
+ rescue
573
+ str = Marshal::dump(make_proxy(obj, error))
574
+ end
575
+ [str.size].pack('N') + str
576
+ end
577
+
578
+ def load(soc) # :nodoc:
579
+ begin
580
+ sz = soc.read(4) # sizeof (N)
581
+ rescue
582
+ raise(DRbConnError, $!.message, $!.backtrace)
583
+ end
584
+ raise(DRbConnError, 'connection closed') if sz.nil?
585
+ raise(DRbConnError, 'premature header') if sz.size < 4
586
+ sz = sz.unpack('N')[0]
587
+ raise(DRbConnError, "too large packet #{sz}") if @load_limit < sz
588
+ begin
589
+ str = soc.read(sz)
590
+ rescue
591
+ raise(DRbConnError, $!.message, $!.backtrace)
592
+ end
593
+ raise(DRbConnError, 'connection closed') if str.nil?
594
+ raise(DRbConnError, 'premature marshal format(can\'t read)') if str.size < sz
595
+ DRb.mutex.synchronize do
596
+ begin
597
+ Marshal::load(str)
598
+ rescue NameError, ArgumentError
599
+ DRbUnknown.new($!, str)
600
+ end
601
+ end
602
+ end
603
+
604
+ def send_request(stream, ref, msg_id, arg, b) # :nodoc:
605
+ ary = []
606
+ ary.push(dump(ref.__drbref))
607
+ ary.push(dump(msg_id.id2name))
608
+ ary.push(dump(arg.length))
609
+ arg.each do |e|
610
+ ary.push(dump(e))
611
+ end
612
+ ary.push(dump(b))
613
+ stream.write(ary.join(''))
614
+ rescue
615
+ raise(DRbConnError, $!.message, $!.backtrace)
616
+ end
617
+
618
+ def recv_request(stream) # :nodoc:
619
+ ref = load(stream)
620
+ ro = DRb.to_obj(ref)
621
+ msg = load(stream)
622
+ argc = load(stream)
623
+ raise(DRbConnError, "too many arguments") if @argc_limit < argc
624
+ argv = Array.new(argc, nil)
625
+ argc.times do |n|
626
+ argv[n] = load(stream)
627
+ end
628
+ block = load(stream)
629
+ return ro, msg, argv, block
630
+ end
631
+
632
+ def send_reply(stream, succ, result) # :nodoc:
633
+ stream.write(dump(succ) + dump(result, !succ))
634
+ rescue
635
+ raise(DRbConnError, $!.message, $!.backtrace)
636
+ end
637
+
638
+ def recv_reply(stream) # :nodoc:
639
+ succ = load(stream)
640
+ result = load(stream)
641
+ [succ, result]
642
+ end
643
+
644
+ private
645
+ def make_proxy(obj, error=false) # :nodoc:
646
+ if error
647
+ DRbRemoteError.new(obj)
648
+ else
649
+ DRbObject.new(obj)
650
+ end
651
+ end
652
+ end
653
+
654
+ # Module managing the underlying network protocol(s) used by drb.
655
+ #
656
+ # By default, drb uses the DRbTCPSocket protocol. Other protocols
657
+ # can be defined. A protocol must define the following class methods:
658
+ #
659
+ # [open(uri, config)] Open a client connection to the server at +uri+,
660
+ # using configuration +config+. Return a protocol
661
+ # instance for this connection.
662
+ # [open_server(uri, config)] Open a server listening at +uri+,
663
+ # using configuration +config+. Return a
664
+ # protocol instance for this listener.
665
+ # [uri_option(uri, config)] Take a URI, possibly containing an option
666
+ # component (e.g. a trailing '?param=val'),
667
+ # and return a [uri, option] tuple.
668
+ #
669
+ # All of these methods should raise a DRbBadScheme error if the URI
670
+ # does not identify the protocol they support (e.g. "druby:" for
671
+ # the standard Ruby protocol). This is how the DRbProtocol module,
672
+ # given a URI, determines which protocol implementation serves that
673
+ # protocol.
674
+ #
675
+ # The protocol instance returned by #open_server must have the
676
+ # following methods:
677
+ #
678
+ # [accept] Accept a new connection to the server. Returns a protocol
679
+ # instance capable of communicating with the client.
680
+ # [close] Close the server connection.
681
+ # [uri] Get the URI for this server.
682
+ #
683
+ # The protocol instance returned by #open must have the following methods:
684
+ #
685
+ # [send_request (ref, msg_id, arg, b)]
686
+ # Send a request to +ref+ with the given message id and arguments.
687
+ # This is most easily implemented by calling DRbMessage.send_request,
688
+ # providing a stream that sits on top of the current protocol.
689
+ # [recv_reply]
690
+ # Receive a reply from the server and return it as a [success-boolean,
691
+ # reply-value] pair. This is most easily implemented by calling
692
+ # DRb.recv_reply, providing a stream that sits on top of the
693
+ # current protocol.
694
+ # [alive?]
695
+ # Is this connection still alive?
696
+ # [close]
697
+ # Close this connection.
698
+ #
699
+ # The protocol instance returned by #open_server().accept() must have
700
+ # the following methods:
701
+ #
702
+ # [recv_request]
703
+ # Receive a request from the client and return a [object, message,
704
+ # args, block] tuple. This is most easily implemented by calling
705
+ # DRbMessage.recv_request, providing a stream that sits on top of
706
+ # the current protocol.
707
+ # [send_reply(succ, result)]
708
+ # Send a reply to the client. This is most easily implemented
709
+ # by calling DRbMessage.send_reply, providing a stream that sits
710
+ # on top of the current protocol.
711
+ # [close]
712
+ # Close this connection.
713
+ #
714
+ # A new protocol is registered with the DRbProtocol module using
715
+ # the add_protocol method.
716
+ #
717
+ # For examples of other protocols, see DRbUNIXSocket in drb/unix.rb,
718
+ # and HTTP0 in sample/http0.rb and sample/http0serv.rb in the full
719
+ # drb distribution.
720
+ module DRbProtocol
721
+
722
+ # Add a new protocol to the DRbProtocol module.
723
+ def add_protocol(prot)
724
+ @protocol.push(prot)
725
+ end
726
+ module_function :add_protocol
727
+
728
+ # Open a client connection to +uri+ with the configuration +config+.
729
+ #
730
+ # The DRbProtocol module asks each registered protocol in turn to
731
+ # try to open the URI. Each protocol signals that it does not handle that
732
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
733
+ # URI, then a DRbBadURI error is raised. If a protocol accepts the
734
+ # URI, but an error occurs in opening it, a DRbConnError is raised.
735
+ def open(uri, config, first=true)
736
+ @protocol.each do |prot|
737
+ begin
738
+ return prot.open(uri, config)
739
+ rescue DRbBadScheme
740
+ rescue DRbConnError
741
+ raise($!)
742
+ rescue
743
+ raise(DRbConnError, "#{uri} - #{$!.inspect}")
744
+ end
745
+ end
746
+ if first && (config[:auto_load] != false)
747
+ auto_load(uri)
748
+ return open(uri, config, false)
749
+ end
750
+ raise DRbBadURI, 'can\'t parse uri:' + uri
751
+ end
752
+ module_function :open
753
+
754
+ # Open a server listening for connections at +uri+ with
755
+ # configuration +config+.
756
+ #
757
+ # The DRbProtocol module asks each registered protocol in turn to
758
+ # try to open a server at the URI. Each protocol signals that it does
759
+ # not handle that URI by raising a DRbBadScheme error. If no protocol
760
+ # recognises the URI, then a DRbBadURI error is raised. If a protocol
761
+ # accepts the URI, but an error occurs in opening it, the underlying
762
+ # error is passed on to the caller.
763
+ def open_server(uri, config, first=true)
764
+ @protocol.each do |prot|
765
+ begin
766
+ return prot.open_server(uri, config)
767
+ rescue DRbBadScheme
768
+ end
769
+ end
770
+ if first && (config[:auto_load] != false)
771
+ auto_load(uri)
772
+ return open_server(uri, config, false)
773
+ end
774
+ raise DRbBadURI, 'can\'t parse uri:' + uri
775
+ end
776
+ module_function :open_server
777
+
778
+ # Parse +uri+ into a [uri, option] pair.
779
+ #
780
+ # The DRbProtocol module asks each registered protocol in turn to
781
+ # try to parse the URI. Each protocol signals that it does not handle that
782
+ # URI by raising a DRbBadScheme error. If no protocol recognises the
783
+ # URI, then a DRbBadURI error is raised.
784
+ def uri_option(uri, config, first=true)
785
+ @protocol.each do |prot|
786
+ begin
787
+ uri, opt = prot.uri_option(uri, config)
788
+ # opt = nil if opt == ''
789
+ return uri, opt
790
+ rescue DRbBadScheme
791
+ end
792
+ end
793
+ if first && (config[:auto_load] != false)
794
+ auto_load(uri)
795
+ return uri_option(uri, config, false)
796
+ end
797
+ raise DRbBadURI, 'can\'t parse uri:' + uri
798
+ end
799
+ module_function :uri_option
800
+
801
+ def auto_load(uri) # :nodoc:
802
+ if /\Adrb([a-z0-9]+):/ =~ uri
803
+ require("drb/#{$1}") rescue nil
804
+ end
805
+ end
806
+ module_function :auto_load
807
+ end
808
+
809
+ # The default drb protocol which communicates over a TCP socket.
810
+ #
811
+ # The DRb TCP protocol URI looks like:
812
+ # <code>druby://<host>:<port>?<option></code>. The option is optional.
813
+
814
+ class DRbTCPSocket
815
+ # :stopdoc:
816
+ private
817
+ def self.parse_uri(uri)
818
+ if /\Adruby:\/\/(.*?):(\d+)(\?(.*))?\z/ =~ uri
819
+ host = $1
820
+ port = $2.to_i
821
+ option = $4
822
+ [host, port, option]
823
+ else
824
+ raise(DRbBadScheme, uri) unless uri.start_with?('druby:')
825
+ raise(DRbBadURI, 'can\'t parse uri:' + uri)
826
+ end
827
+ end
828
+
829
+ public
830
+
831
+ # Open a client connection to +uri+ (DRb URI string) using configuration
832
+ # +config+.
833
+ #
834
+ # This can raise DRb::DRbBadScheme or DRb::DRbBadURI if +uri+ is not for a
835
+ # recognized protocol. See DRb::DRbServer.new for information on built-in
836
+ # URI protocols.
837
+ def self.open(uri, config)
838
+ host, port, = parse_uri(uri)
839
+ soc = TCPSocket.open(host, port)
840
+ self.new(uri, soc, config)
841
+ end
842
+
843
+ # Returns the hostname of this server
844
+ def self.getservername
845
+ host = Socket::gethostname
846
+ begin
847
+ Socket::getaddrinfo(host, nil,
848
+ Socket::AF_UNSPEC,
849
+ Socket::SOCK_STREAM,
850
+ 0,
851
+ Socket::AI_PASSIVE)[0][3]
852
+ rescue
853
+ 'localhost'
854
+ end
855
+ end
856
+
857
+ # For the families available for +host+, returns a TCPServer on +port+.
858
+ # If +port+ is 0 the first available port is used. IPv4 servers are
859
+ # preferred over IPv6 servers.
860
+ def self.open_server_inaddr_any(host, port)
861
+ infos = Socket::getaddrinfo(host, nil,
862
+ Socket::AF_UNSPEC,
863
+ Socket::SOCK_STREAM,
864
+ 0,
865
+ Socket::AI_PASSIVE)
866
+ families = Hash[*infos.collect { |af, *_| af }.uniq.zip([]).flatten]
867
+ return TCPServer.open('0.0.0.0', port) if families.has_key?('AF_INET')
868
+ return TCPServer.open('::', port) if families.has_key?('AF_INET6')
869
+ return TCPServer.open(port)
870
+ # :stopdoc:
871
+ end
872
+
873
+ # Open a server listening for connections at +uri+ using
874
+ # configuration +config+.
875
+ def self.open_server(uri, config)
876
+ uri = 'druby://:0' unless uri
877
+ host, port, _ = parse_uri(uri)
878
+ config = {:tcp_original_host => host}.update(config)
879
+ if host.size == 0
880
+ host = getservername
881
+ soc = open_server_inaddr_any(host, port)
882
+ else
883
+ soc = TCPServer.open(host, port)
884
+ end
885
+ port = soc.addr[1] if port == 0
886
+ config[:tcp_port] = port
887
+ uri = "druby://#{host}:#{port}"
888
+ self.new(uri, soc, config)
889
+ end
890
+
891
+ # Parse +uri+ into a [uri, option] pair.
892
+ def self.uri_option(uri, config)
893
+ host, port, option = parse_uri(uri)
894
+ return "druby://#{host}:#{port}", option
895
+ end
896
+
897
+ # Create a new DRbTCPSocket instance.
898
+ #
899
+ # +uri+ is the URI we are connected to.
900
+ # +soc+ is the tcp socket we are bound to. +config+ is our
901
+ # configuration.
902
+ def initialize(uri, soc, config={})
903
+ @uri = uri
904
+ @socket = soc
905
+ @config = config
906
+ @acl = config[:tcp_acl]
907
+ @msg = DRbMessage.new(config)
908
+ set_sockopt(@socket)
909
+ @shutdown_pipe_r, @shutdown_pipe_w = IO.pipe
910
+ end
911
+
912
+ # Get the URI that we are connected to.
913
+ attr_reader :uri
914
+
915
+ # Get the address of our TCP peer (the other end of the socket
916
+ # we are bound to.
917
+ def peeraddr
918
+ @socket.peeraddr
919
+ end
920
+
921
+ # Get the socket.
922
+ def stream; @socket; end
923
+
924
+ # On the client side, send a request to the server.
925
+ def send_request(ref, msg_id, arg, b)
926
+ @msg.send_request(stream, ref, msg_id, arg, b)
927
+ end
928
+
929
+ # On the server side, receive a request from the client.
930
+ def recv_request
931
+ @msg.recv_request(stream)
932
+ end
933
+
934
+ # On the server side, send a reply to the client.
935
+ def send_reply(succ, result)
936
+ @msg.send_reply(stream, succ, result)
937
+ end
938
+
939
+ # On the client side, receive a reply from the server.
940
+ def recv_reply
941
+ @msg.recv_reply(stream)
942
+ end
943
+
944
+ public
945
+
946
+ # Close the connection.
947
+ #
948
+ # If this is an instance returned by #open_server, then this stops
949
+ # listening for new connections altogether. If this is an instance
950
+ # returned by #open or by #accept, then it closes this particular
951
+ # client-server session.
952
+ def close
953
+ shutdown
954
+ if @socket
955
+ @socket.close
956
+ @socket = nil
957
+ end
958
+ close_shutdown_pipe
959
+ end
960
+
961
+ def close_shutdown_pipe
962
+ @shutdown_pipe_w.close
963
+ @shutdown_pipe_r.close
964
+ end
965
+ private :close_shutdown_pipe
966
+
967
+ # On the server side, for an instance returned by #open_server,
968
+ # accept a client connection and return a new instance to handle
969
+ # the server's side of this client-server session.
970
+ def accept
971
+ while true
972
+ s = accept_or_shutdown
973
+ return nil unless s
974
+ break if (@acl ? @acl.allow_socket?(s) : true)
975
+ s.close
976
+ end
977
+ if @config[:tcp_original_host].to_s.size == 0
978
+ uri = "druby://#{s.addr[3]}:#{@config[:tcp_port]}"
979
+ else
980
+ uri = @uri
981
+ end
982
+ self.class.new(uri, s, @config)
983
+ end
984
+
985
+ def accept_or_shutdown
986
+ readables, = IO.select([@socket, @shutdown_pipe_r])
987
+ if readables.include? @shutdown_pipe_r
988
+ return nil
989
+ end
990
+ @socket.accept
991
+ end
992
+ private :accept_or_shutdown
993
+
994
+ # Graceful shutdown
995
+ def shutdown
996
+ @shutdown_pipe_w.close
997
+ end
998
+
999
+ # Check to see if this connection is alive.
1000
+ def alive?
1001
+ return false unless @socket
1002
+ if @socket.to_io.wait_readable(0)
1003
+ close
1004
+ return false
1005
+ end
1006
+ true
1007
+ end
1008
+
1009
+ def set_sockopt(soc) # :nodoc:
1010
+ soc.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
1011
+ rescue IOError, Errno::ECONNRESET, Errno::EINVAL
1012
+ # closed/shutdown socket, ignore error
1013
+ end
1014
+ end
1015
+
1016
+ module DRbProtocol
1017
+ @protocol = [DRbTCPSocket] # default
1018
+ end
1019
+
1020
+ class DRbURIOption # :nodoc: I don't understand the purpose of this class...
1021
+ def initialize(option)
1022
+ @option = option.to_s
1023
+ end
1024
+ attr_reader :option
1025
+ def to_s; @option; end
1026
+
1027
+ def ==(other)
1028
+ return false unless DRbURIOption === other
1029
+ @option == other.option
1030
+ end
1031
+
1032
+ def hash
1033
+ @option.hash
1034
+ end
1035
+
1036
+ alias eql? ==
1037
+ end
1038
+
1039
+ # Object wrapping a reference to a remote drb object.
1040
+ #
1041
+ # Method calls on this object are relayed to the remote
1042
+ # object that this object is a stub for.
1043
+ class DRbObject
1044
+
1045
+ # Unmarshall a marshalled DRbObject.
1046
+ #
1047
+ # If the referenced object is located within the local server, then
1048
+ # the object itself is returned. Otherwise, a new DRbObject is
1049
+ # created to act as a stub for the remote referenced object.
1050
+ def self._load(s)
1051
+ uri, ref = Marshal.load(s)
1052
+
1053
+ if DRb.here?(uri)
1054
+ obj = DRb.to_obj(ref)
1055
+ return obj
1056
+ end
1057
+
1058
+ self.new_with(uri, ref)
1059
+ end
1060
+
1061
+ # Creates a DRb::DRbObject given the reference information to the remote
1062
+ # host +uri+ and object +ref+.
1063
+
1064
+ def self.new_with(uri, ref)
1065
+ it = self.allocate
1066
+ it.instance_variable_set(:@uri, uri)
1067
+ it.instance_variable_set(:@ref, ref)
1068
+ it
1069
+ end
1070
+
1071
+ # Create a new DRbObject from a URI alone.
1072
+ def self.new_with_uri(uri)
1073
+ self.new(nil, uri)
1074
+ end
1075
+
1076
+ # Marshall this object.
1077
+ #
1078
+ # The URI and ref of the object are marshalled.
1079
+ def _dump(lv)
1080
+ Marshal.dump([@uri, @ref])
1081
+ end
1082
+
1083
+ # Create a new remote object stub.
1084
+ #
1085
+ # +obj+ is the (local) object we want to create a stub for. Normally
1086
+ # this is +nil+. +uri+ is the URI of the remote object that this
1087
+ # will be a stub for.
1088
+ def initialize(obj, uri=nil)
1089
+ @uri = nil
1090
+ @ref = nil
1091
+ case obj
1092
+ when Object
1093
+ is_nil = obj.nil?
1094
+ when BasicObject
1095
+ is_nil = false
1096
+ end
1097
+
1098
+ if is_nil
1099
+ return if uri.nil?
1100
+ @uri, option = DRbProtocol.uri_option(uri, DRb.config)
1101
+ @ref = DRbURIOption.new(option) unless option.nil?
1102
+ else
1103
+ @uri = uri ? uri : (DRb.uri rescue nil)
1104
+ @ref = obj ? DRb.to_id(obj) : nil
1105
+ end
1106
+ end
1107
+
1108
+ # Get the URI of the remote object.
1109
+ def __drburi
1110
+ @uri
1111
+ end
1112
+
1113
+ # Get the reference of the object, if local.
1114
+ def __drbref
1115
+ @ref
1116
+ end
1117
+
1118
+ undef :to_s
1119
+ undef :to_a if respond_to?(:to_a)
1120
+
1121
+ # Routes respond_to? to the referenced remote object.
1122
+ def respond_to?(msg_id, priv=false)
1123
+ case msg_id
1124
+ when :_dump
1125
+ true
1126
+ when :marshal_dump
1127
+ false
1128
+ else
1129
+ method_missing(:respond_to?, msg_id, priv)
1130
+ end
1131
+ end
1132
+
1133
+ # Routes method calls to the referenced remote object.
1134
+ ruby2_keywords def method_missing(msg_id, *a, &b)
1135
+ if DRb.here?(@uri)
1136
+ obj = DRb.to_obj(@ref)
1137
+ DRb.current_server.check_insecure_method(obj, msg_id)
1138
+ return obj.__send__(msg_id, *a, &b)
1139
+ end
1140
+
1141
+ succ, result = self.class.with_friend(@uri) do
1142
+ DRbConn.open(@uri) do |conn|
1143
+ conn.send_message(self, msg_id, a, b)
1144
+ end
1145
+ end
1146
+
1147
+ if succ
1148
+ return result
1149
+ elsif DRbUnknown === result
1150
+ raise result
1151
+ else
1152
+ bt = self.class.prepare_backtrace(@uri, result)
1153
+ result.set_backtrace(bt + caller)
1154
+ raise result
1155
+ end
1156
+ end
1157
+
1158
+ # Given the +uri+ of another host executes the block provided.
1159
+ def self.with_friend(uri) # :nodoc:
1160
+ friend = DRb.fetch_server(uri)
1161
+ return yield() unless friend
1162
+
1163
+ save = Thread.current['DRb']
1164
+ Thread.current['DRb'] = { 'server' => friend }
1165
+ return yield
1166
+ ensure
1167
+ Thread.current['DRb'] = save if friend
1168
+ end
1169
+
1170
+ # Returns a modified backtrace from +result+ with the +uri+ where each call
1171
+ # in the backtrace came from.
1172
+ def self.prepare_backtrace(uri, result) # :nodoc:
1173
+ prefix = "(#{uri}) "
1174
+ bt = []
1175
+ result.backtrace.each do |x|
1176
+ break if /`__send__'$/ =~ x
1177
+ if /\A\(druby:\/\// =~ x
1178
+ bt.push(x)
1179
+ else
1180
+ bt.push(prefix + x)
1181
+ end
1182
+ end
1183
+ bt
1184
+ end
1185
+
1186
+ def pretty_print(q) # :nodoc:
1187
+ q.pp_object(self)
1188
+ end
1189
+
1190
+ def pretty_print_cycle(q) # :nodoc:
1191
+ q.object_address_group(self) {
1192
+ q.breakable
1193
+ q.text '...'
1194
+ }
1195
+ end
1196
+ end
1197
+
1198
+ class ThreadObject
1199
+ include MonitorMixin
1200
+
1201
+ def initialize(&blk)
1202
+ super()
1203
+ @wait_ev = new_cond
1204
+ @req_ev = new_cond
1205
+ @res_ev = new_cond
1206
+ @status = :wait
1207
+ @req = nil
1208
+ @res = nil
1209
+ @thread = Thread.new(self, &blk)
1210
+ end
1211
+
1212
+ def alive?
1213
+ @thread.alive?
1214
+ end
1215
+
1216
+ def kill
1217
+ @thread.kill
1218
+ @thread.join
1219
+ end
1220
+
1221
+ def method_missing(msg, *arg, &blk)
1222
+ synchronize do
1223
+ @wait_ev.wait_until { @status == :wait }
1224
+ @req = [msg] + arg
1225
+ @status = :req
1226
+ @req_ev.broadcast
1227
+ @res_ev.wait_until { @status == :res }
1228
+ value = @res
1229
+ @req = @res = nil
1230
+ @status = :wait
1231
+ @wait_ev.broadcast
1232
+ return value
1233
+ end
1234
+ end
1235
+
1236
+ def _execute()
1237
+ synchronize do
1238
+ @req_ev.wait_until { @status == :req }
1239
+ @res = yield(@req)
1240
+ @status = :res
1241
+ @res_ev.signal
1242
+ end
1243
+ end
1244
+ end
1245
+
1246
+ # Class handling the connection between a DRbObject and the
1247
+ # server the real object lives on.
1248
+ #
1249
+ # This class maintains a pool of connections, to reduce the
1250
+ # overhead of starting and closing down connections for each
1251
+ # method call.
1252
+ #
1253
+ # This class is used internally by DRbObject. The user does
1254
+ # not normally need to deal with it directly.
1255
+ class DRbConn
1256
+ POOL_SIZE = 16 # :nodoc:
1257
+
1258
+ def self.make_pool
1259
+ ThreadObject.new do |queue|
1260
+ pool = []
1261
+ while true
1262
+ queue._execute do |message|
1263
+ case(message[0])
1264
+ when :take then
1265
+ remote_uri = message[1]
1266
+ conn = nil
1267
+ new_pool = []
1268
+ pool.each do |c|
1269
+ if conn.nil? and c.uri == remote_uri
1270
+ conn = c if c.alive?
1271
+ else
1272
+ new_pool.push c
1273
+ end
1274
+ end
1275
+ pool = new_pool
1276
+ conn
1277
+ when :store then
1278
+ conn = message[1]
1279
+ pool.unshift(conn)
1280
+ pool.pop.close while pool.size > POOL_SIZE
1281
+ conn
1282
+ else
1283
+ nil
1284
+ end
1285
+ end
1286
+ end
1287
+ end
1288
+ end
1289
+ @pool_proxy = nil
1290
+
1291
+ def self.stop_pool
1292
+ @pool_proxy&.kill
1293
+ @pool_proxy = nil
1294
+ end
1295
+
1296
+ def self.open(remote_uri) # :nodoc:
1297
+ begin
1298
+ @pool_proxy = make_pool unless @pool_proxy&.alive?
1299
+
1300
+ conn = @pool_proxy.take(remote_uri)
1301
+ conn = self.new(remote_uri) unless conn
1302
+ succ, result = yield(conn)
1303
+ return succ, result
1304
+
1305
+ ensure
1306
+ if conn
1307
+ if succ
1308
+ @pool_proxy.store(conn)
1309
+ else
1310
+ conn.close
1311
+ end
1312
+ end
1313
+ end
1314
+ end
1315
+
1316
+ def initialize(remote_uri) # :nodoc:
1317
+ @uri = remote_uri
1318
+ @protocol = DRbProtocol.open(remote_uri, DRb.config)
1319
+ end
1320
+ attr_reader :uri # :nodoc:
1321
+
1322
+ def send_message(ref, msg_id, arg, block) # :nodoc:
1323
+ @protocol.send_request(ref, msg_id, arg, block)
1324
+ @protocol.recv_reply
1325
+ end
1326
+
1327
+ def close # :nodoc:
1328
+ @protocol.close
1329
+ @protocol = nil
1330
+ end
1331
+
1332
+ def alive? # :nodoc:
1333
+ return false unless @protocol
1334
+ @protocol.alive?
1335
+ end
1336
+ end
1337
+
1338
+ # Class representing a drb server instance.
1339
+ #
1340
+ # A DRbServer must be running in the local process before any incoming
1341
+ # dRuby calls can be accepted, or any local objects can be passed as
1342
+ # dRuby references to remote processes, even if those local objects are
1343
+ # never actually called remotely. You do not need to start a DRbServer
1344
+ # in the local process if you are only making outgoing dRuby calls
1345
+ # passing marshalled parameters.
1346
+ #
1347
+ # Unless multiple servers are being used, the local DRbServer is normally
1348
+ # started by calling DRb.start_service.
1349
+ class DRbServer
1350
+ @@acl = nil
1351
+ @@idconv = DRbIdConv.new
1352
+ @@secondary_server = nil
1353
+ @@argc_limit = 256
1354
+ @@load_limit = 0xffffffff
1355
+ @@verbose = false
1356
+
1357
+ # Set the default value for the :argc_limit option.
1358
+ #
1359
+ # See #new(). The initial default value is 256.
1360
+ def self.default_argc_limit(argc)
1361
+ @@argc_limit = argc
1362
+ end
1363
+
1364
+ # Set the default value for the :load_limit option.
1365
+ #
1366
+ # See #new(). The initial default value is 25 MB.
1367
+ def self.default_load_limit(sz)
1368
+ @@load_limit = sz
1369
+ end
1370
+
1371
+ # Set the default access control list to +acl+. The default ACL is +nil+.
1372
+ #
1373
+ # See also DRb::ACL and #new()
1374
+ def self.default_acl(acl)
1375
+ @@acl = acl
1376
+ end
1377
+
1378
+ # Set the default value for the :id_conv option.
1379
+ #
1380
+ # See #new(). The initial default value is a DRbIdConv instance.
1381
+ def self.default_id_conv(idconv)
1382
+ @@idconv = idconv
1383
+ end
1384
+
1385
+ # Set the default value of the :verbose option.
1386
+ #
1387
+ # See #new(). The initial default value is false.
1388
+ def self.verbose=(on)
1389
+ @@verbose = on
1390
+ end
1391
+
1392
+ # Get the default value of the :verbose option.
1393
+ def self.verbose
1394
+ @@verbose
1395
+ end
1396
+
1397
+ def self.make_config(hash={}) # :nodoc:
1398
+ default_config = {
1399
+ :idconv => @@idconv,
1400
+ :verbose => @@verbose,
1401
+ :tcp_acl => @@acl,
1402
+ :load_limit => @@load_limit,
1403
+ :argc_limit => @@argc_limit,
1404
+ }
1405
+ default_config.update(hash)
1406
+ end
1407
+
1408
+ # Create a new DRbServer instance.
1409
+ #
1410
+ # +uri+ is the URI to bind to. This is normally of the form
1411
+ # 'druby://<hostname>:<port>' where <hostname> is a hostname of
1412
+ # the local machine. If nil, then the system's default hostname
1413
+ # will be bound to, on a port selected by the system; these value
1414
+ # can be retrieved from the +uri+ attribute. 'druby:' specifies
1415
+ # the default dRuby transport protocol: another protocol, such
1416
+ # as 'drbunix:', can be specified instead.
1417
+ #
1418
+ # +front+ is the front object for the server, that is, the object
1419
+ # to which remote method calls on the server will be passed. If
1420
+ # nil, then the server will not accept remote method calls.
1421
+ #
1422
+ # If +config_or_acl+ is a hash, it is the configuration to
1423
+ # use for this server. The following options are recognised:
1424
+ #
1425
+ # :idconv :: an id-to-object conversion object. This defaults
1426
+ # to an instance of the class DRb::DRbIdConv.
1427
+ # :verbose :: if true, all unsuccessful remote calls on objects
1428
+ # in the server will be logged to $stdout. false
1429
+ # by default.
1430
+ # :tcp_acl :: the access control list for this server. See
1431
+ # the ACL class from the main dRuby distribution.
1432
+ # :load_limit :: the maximum message size in bytes accepted by
1433
+ # the server. Defaults to 25 MB (26214400).
1434
+ # :argc_limit :: the maximum number of arguments to a remote
1435
+ # method accepted by the server. Defaults to
1436
+ # 256.
1437
+ # The default values of these options can be modified on
1438
+ # a class-wide basis by the class methods #default_argc_limit,
1439
+ # #default_load_limit, #default_acl, #default_id_conv,
1440
+ # and #verbose=
1441
+ #
1442
+ # If +config_or_acl+ is not a hash, but is not nil, it is
1443
+ # assumed to be the access control list for this server.
1444
+ # See the :tcp_acl option for more details.
1445
+ #
1446
+ # If no other server is currently set as the primary server,
1447
+ # this will become the primary server.
1448
+ #
1449
+ # The server will immediately start running in its own thread.
1450
+ def initialize(uri=nil, front=nil, config_or_acl=nil)
1451
+ if Hash === config_or_acl
1452
+ config = config_or_acl.dup
1453
+ else
1454
+ acl = config_or_acl || @@acl
1455
+ config = {
1456
+ :tcp_acl => acl
1457
+ }
1458
+ end
1459
+
1460
+ @config = self.class.make_config(config)
1461
+
1462
+ @protocol = DRbProtocol.open_server(uri, @config)
1463
+ @uri = @protocol.uri
1464
+ @exported_uri = [@uri]
1465
+
1466
+ @front = front
1467
+ @idconv = @config[:idconv]
1468
+
1469
+ @grp = ThreadGroup.new
1470
+ @thread = run
1471
+
1472
+ DRb.regist_server(self)
1473
+ end
1474
+
1475
+ # The URI of this DRbServer.
1476
+ attr_reader :uri
1477
+
1478
+ # The main thread of this DRbServer.
1479
+ #
1480
+ # This is the thread that listens for and accepts connections
1481
+ # from clients, not that handles each client's request-response
1482
+ # session.
1483
+ attr_reader :thread
1484
+
1485
+ # The front object of the DRbServer.
1486
+ #
1487
+ # This object receives remote method calls made on the server's
1488
+ # URI alone, with an object id.
1489
+ attr_reader :front
1490
+
1491
+ # The configuration of this DRbServer
1492
+ attr_reader :config
1493
+
1494
+ # Set whether to operate in verbose mode.
1495
+ #
1496
+ # In verbose mode, failed calls are logged to stdout.
1497
+ def verbose=(v); @config[:verbose]=v; end
1498
+
1499
+ # Get whether the server is in verbose mode.
1500
+ #
1501
+ # In verbose mode, failed calls are logged to stdout.
1502
+ def verbose; @config[:verbose]; end
1503
+
1504
+ # Is this server alive?
1505
+ def alive?
1506
+ @thread.alive?
1507
+ end
1508
+
1509
+ # Is +uri+ the URI for this server?
1510
+ def here?(uri)
1511
+ @exported_uri.include?(uri)
1512
+ end
1513
+
1514
+ # Stop this server.
1515
+ def stop_service
1516
+ DRb.remove_server(self)
1517
+ if Thread.current['DRb'] && Thread.current['DRb']['server'] == self
1518
+ Thread.current['DRb']['stop_service'] = true
1519
+ else
1520
+ shutdown
1521
+ end
1522
+ end
1523
+
1524
+ # Convert a dRuby reference to the local object it refers to.
1525
+ def to_obj(ref)
1526
+ return front if ref.nil?
1527
+ return front[ref.to_s] if DRbURIOption === ref
1528
+ @idconv.to_obj(ref)
1529
+ end
1530
+
1531
+ # Convert a local object to a dRuby reference.
1532
+ def to_id(obj)
1533
+ return nil if obj.__id__ == front.__id__
1534
+ @idconv.to_id(obj)
1535
+ end
1536
+
1537
+ private
1538
+
1539
+ def shutdown
1540
+ current = Thread.current
1541
+ if @protocol.respond_to? :shutdown
1542
+ @protocol.shutdown
1543
+ else
1544
+ [@thread, *@grp.list].each { |thread|
1545
+ thread.kill unless thread == current # xxx: Thread#kill
1546
+ }
1547
+ end
1548
+ @thread.join unless @thread == current
1549
+ end
1550
+
1551
+ ##
1552
+ # Starts the DRb main loop in a new thread.
1553
+
1554
+ def run
1555
+ Thread.start do
1556
+ begin
1557
+ while main_loop
1558
+ end
1559
+ ensure
1560
+ @protocol.close if @protocol
1561
+ end
1562
+ end
1563
+ end
1564
+
1565
+ # List of insecure methods.
1566
+ #
1567
+ # These methods are not callable via dRuby.
1568
+ INSECURE_METHOD = [
1569
+ :__send__
1570
+ ]
1571
+
1572
+ # Has a method been included in the list of insecure methods?
1573
+ def insecure_method?(msg_id)
1574
+ INSECURE_METHOD.include?(msg_id)
1575
+ end
1576
+
1577
+ # Coerce an object to a string, providing our own representation if
1578
+ # to_s is not defined for the object.
1579
+ def any_to_s(obj)
1580
+ "#{obj}:#{obj.class}"
1581
+ rescue
1582
+ Kernel.instance_method(:to_s).bind_call(obj)
1583
+ end
1584
+
1585
+ # Check that a method is callable via dRuby.
1586
+ #
1587
+ # +obj+ is the object we want to invoke the method on. +msg_id+ is the
1588
+ # method name, as a Symbol.
1589
+ #
1590
+ # If the method is an insecure method (see #insecure_method?) a
1591
+ # SecurityError is thrown. If the method is private or undefined,
1592
+ # a NameError is thrown.
1593
+ def check_insecure_method(obj, msg_id)
1594
+ return true if Proc === obj && msg_id == :__drb_yield
1595
+ raise(ArgumentError, "#{any_to_s(msg_id)} is not a symbol") unless Symbol == msg_id.class
1596
+ raise(SecurityError, "insecure method `#{msg_id}'") if insecure_method?(msg_id)
1597
+
1598
+ case obj
1599
+ when Object
1600
+ if obj.private_methods.include?(msg_id)
1601
+ desc = any_to_s(obj)
1602
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
1603
+ elsif obj.protected_methods.include?(msg_id)
1604
+ desc = any_to_s(obj)
1605
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
1606
+ else
1607
+ true
1608
+ end
1609
+ else
1610
+ if Kernel.instance_method(:private_methods).bind(obj).call.include?(msg_id)
1611
+ desc = any_to_s(obj)
1612
+ raise NoMethodError, "private method `#{msg_id}' called for #{desc}"
1613
+ elsif Kernel.instance_method(:protected_methods).bind(obj).call.include?(msg_id)
1614
+ desc = any_to_s(obj)
1615
+ raise NoMethodError, "protected method `#{msg_id}' called for #{desc}"
1616
+ else
1617
+ true
1618
+ end
1619
+ end
1620
+ end
1621
+ public :check_insecure_method
1622
+
1623
+ class InvokeMethod # :nodoc:
1624
+ def initialize(drb_server, client)
1625
+ @drb_server = drb_server
1626
+ @client = client
1627
+ end
1628
+
1629
+ def perform
1630
+ @result = nil
1631
+ @succ = false
1632
+ setup_message
1633
+
1634
+ if @block
1635
+ @result = perform_with_block
1636
+ else
1637
+ @result = perform_without_block
1638
+ end
1639
+ @succ = true
1640
+ case @result
1641
+ when Array
1642
+ if @msg_id == :to_ary
1643
+ @result = DRbArray.new(@result)
1644
+ end
1645
+ end
1646
+ return @succ, @result
1647
+ rescue NoMemoryError, SystemExit, SystemStackError, SecurityError
1648
+ raise
1649
+ rescue Exception
1650
+ @result = $!
1651
+ return @succ, @result
1652
+ end
1653
+
1654
+ private
1655
+ def init_with_client
1656
+ obj, msg, argv, block = @client.recv_request
1657
+ @obj = obj
1658
+ @msg_id = msg.intern
1659
+ @argv = argv
1660
+ @block = block
1661
+ end
1662
+
1663
+ def check_insecure_method
1664
+ @drb_server.check_insecure_method(@obj, @msg_id)
1665
+ end
1666
+
1667
+ def setup_message
1668
+ init_with_client
1669
+ check_insecure_method
1670
+ end
1671
+
1672
+ def perform_without_block
1673
+ if Proc === @obj && @msg_id == :__drb_yield
1674
+ if @argv.size == 1
1675
+ ary = @argv
1676
+ else
1677
+ ary = [@argv]
1678
+ end
1679
+ ary.collect(&@obj)[0]
1680
+ else
1681
+ @obj.__send__(@msg_id, *@argv)
1682
+ end
1683
+ end
1684
+
1685
+ end
1686
+
1687
+ require_relative 'invokemethod'
1688
+ class InvokeMethod
1689
+ include InvokeMethod18Mixin
1690
+ end
1691
+
1692
+ def error_print(exception)
1693
+ exception.backtrace.inject(true) do |first, x|
1694
+ if first
1695
+ $stderr.puts "#{x}: #{exception} (#{exception.class})"
1696
+ else
1697
+ $stderr.puts "\tfrom #{x}"
1698
+ end
1699
+ false
1700
+ end
1701
+ end
1702
+
1703
+ # The main loop performed by a DRbServer's internal thread.
1704
+ #
1705
+ # Accepts a connection from a client, and starts up its own
1706
+ # thread to handle it. This thread loops, receiving requests
1707
+ # from the client, invoking them on a local object, and
1708
+ # returning responses, until the client closes the connection
1709
+ # or a local method call fails.
1710
+ def main_loop
1711
+ client0 = @protocol.accept
1712
+ return nil if !client0
1713
+ Thread.start(client0) do |client|
1714
+ @grp.add Thread.current
1715
+ Thread.current['DRb'] = { 'client' => client ,
1716
+ 'server' => self }
1717
+ DRb.mutex.synchronize do
1718
+ client_uri = client.uri
1719
+ @exported_uri << client_uri unless @exported_uri.include?(client_uri)
1720
+ end
1721
+ loop do
1722
+ begin
1723
+ succ = false
1724
+ invoke_method = InvokeMethod.new(self, client)
1725
+ succ, result = invoke_method.perform
1726
+ error_print(result) if !succ && verbose
1727
+ unless DRbConnError === result && result.message == 'connection closed'
1728
+ client.send_reply(succ, result)
1729
+ end
1730
+ rescue Exception => e
1731
+ error_print(e) if verbose
1732
+ ensure
1733
+ client.close unless succ
1734
+ if Thread.current['DRb']['stop_service']
1735
+ shutdown
1736
+ break
1737
+ end
1738
+ break unless succ
1739
+ end
1740
+ end
1741
+ end
1742
+ end
1743
+ end
1744
+
1745
+ @primary_server = nil
1746
+
1747
+ # Start a dRuby server locally.
1748
+ #
1749
+ # The new dRuby server will become the primary server, even
1750
+ # if another server is currently the primary server.
1751
+ #
1752
+ # +uri+ is the URI for the server to bind to. If nil,
1753
+ # the server will bind to random port on the default local host
1754
+ # name and use the default dRuby protocol.
1755
+ #
1756
+ # +front+ is the server's front object. This may be nil.
1757
+ #
1758
+ # +config+ is the configuration for the new server. This may
1759
+ # be nil.
1760
+ #
1761
+ # See DRbServer::new.
1762
+ def start_service(uri=nil, front=nil, config=nil)
1763
+ @primary_server = DRbServer.new(uri, front, config)
1764
+ end
1765
+ module_function :start_service
1766
+
1767
+ # The primary local dRuby server.
1768
+ #
1769
+ # This is the server created by the #start_service call.
1770
+ attr_accessor :primary_server
1771
+ module_function :primary_server=, :primary_server
1772
+
1773
+ # Get the 'current' server.
1774
+ #
1775
+ # In the context of execution taking place within the main
1776
+ # thread of a dRuby server (typically, as a result of a remote
1777
+ # call on the server or one of its objects), the current
1778
+ # server is that server. Otherwise, the current server is
1779
+ # the primary server.
1780
+ #
1781
+ # If the above rule fails to find a server, a DRbServerNotFound
1782
+ # error is raised.
1783
+ def current_server
1784
+ drb = Thread.current['DRb']
1785
+ server = (drb && drb['server']) ? drb['server'] : @primary_server
1786
+ raise DRbServerNotFound unless server
1787
+ return server
1788
+ end
1789
+ module_function :current_server
1790
+
1791
+ # Stop the local dRuby server.
1792
+ #
1793
+ # This operates on the primary server. If there is no primary
1794
+ # server currently running, it is a noop.
1795
+ def stop_service
1796
+ @primary_server.stop_service if @primary_server
1797
+ @primary_server = nil
1798
+ end
1799
+ module_function :stop_service
1800
+
1801
+ # Get the URI defining the local dRuby space.
1802
+ #
1803
+ # This is the URI of the current server. See #current_server.
1804
+ def uri
1805
+ drb = Thread.current['DRb']
1806
+ client = (drb && drb['client'])
1807
+ if client
1808
+ uri = client.uri
1809
+ return uri if uri
1810
+ end
1811
+ current_server.uri
1812
+ end
1813
+ module_function :uri
1814
+
1815
+ # Is +uri+ the URI for the current local server?
1816
+ def here?(uri)
1817
+ current_server.here?(uri) rescue false
1818
+ # (current_server.uri rescue nil) == uri
1819
+ end
1820
+ module_function :here?
1821
+
1822
+ # Get the configuration of the current server.
1823
+ #
1824
+ # If there is no current server, this returns the default configuration.
1825
+ # See #current_server and DRbServer::make_config.
1826
+ def config
1827
+ current_server.config
1828
+ rescue
1829
+ DRbServer.make_config
1830
+ end
1831
+ module_function :config
1832
+
1833
+ # Get the front object of the current server.
1834
+ #
1835
+ # This raises a DRbServerNotFound error if there is no current server.
1836
+ # See #current_server.
1837
+ def front
1838
+ current_server.front
1839
+ end
1840
+ module_function :front
1841
+
1842
+ # Convert a reference into an object using the current server.
1843
+ #
1844
+ # This raises a DRbServerNotFound error if there is no current server.
1845
+ # See #current_server.
1846
+ def to_obj(ref)
1847
+ current_server.to_obj(ref)
1848
+ end
1849
+
1850
+ # Get a reference id for an object using the current server.
1851
+ #
1852
+ # This raises a DRbServerNotFound error if there is no current server.
1853
+ # See #current_server.
1854
+ def to_id(obj)
1855
+ current_server.to_id(obj)
1856
+ end
1857
+ module_function :to_id
1858
+ module_function :to_obj
1859
+
1860
+ # Get the thread of the primary server.
1861
+ #
1862
+ # This returns nil if there is no primary server. See #primary_server.
1863
+ def thread
1864
+ @primary_server ? @primary_server.thread : nil
1865
+ end
1866
+ module_function :thread
1867
+
1868
+ # Set the default id conversion object.
1869
+ #
1870
+ # This is expected to be an instance such as DRb::DRbIdConv that responds to
1871
+ # #to_id and #to_obj that can convert objects to and from DRb references.
1872
+ #
1873
+ # See DRbServer#default_id_conv.
1874
+ def install_id_conv(idconv)
1875
+ DRbServer.default_id_conv(idconv)
1876
+ end
1877
+ module_function :install_id_conv
1878
+
1879
+ # Set the default ACL to +acl+.
1880
+ #
1881
+ # See DRb::DRbServer.default_acl.
1882
+ def install_acl(acl)
1883
+ DRbServer.default_acl(acl)
1884
+ end
1885
+ module_function :install_acl
1886
+
1887
+ @mutex = Thread::Mutex.new
1888
+ def mutex # :nodoc:
1889
+ @mutex
1890
+ end
1891
+ module_function :mutex
1892
+
1893
+ @server = {}
1894
+ # Registers +server+ with DRb.
1895
+ #
1896
+ # This is called when a new DRb::DRbServer is created.
1897
+ #
1898
+ # If there is no primary server then +server+ becomes the primary server.
1899
+ #
1900
+ # Example:
1901
+ #
1902
+ # require 'drb'
1903
+ #
1904
+ # s = DRb::DRbServer.new # automatically calls regist_server
1905
+ # DRb.fetch_server s.uri #=> #<DRb::DRbServer:0x...>
1906
+ def regist_server(server)
1907
+ @server[server.uri] = server
1908
+ mutex.synchronize do
1909
+ @primary_server = server unless @primary_server
1910
+ end
1911
+ end
1912
+ module_function :regist_server
1913
+
1914
+ # Removes +server+ from the list of registered servers.
1915
+ def remove_server(server)
1916
+ @server.delete(server.uri)
1917
+ mutex.synchronize do
1918
+ if @primary_server == server
1919
+ @primary_server = nil
1920
+ end
1921
+ end
1922
+ end
1923
+ module_function :remove_server
1924
+
1925
+ # Retrieves the server with the given +uri+.
1926
+ #
1927
+ # See also regist_server and remove_server.
1928
+ def fetch_server(uri)
1929
+ @server[uri]
1930
+ end
1931
+ module_function :fetch_server
1932
+ end
1933
+
1934
+ # :stopdoc:
1935
+ DRbObject = DRb::DRbObject
1936
+ DRbUndumped = DRb::DRbUndumped
1937
+ DRbIdConv = DRb::DRbIdConv