drb 2.0.4

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,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