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