PageRenderTime 45ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/drb/drb.rb

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