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